Only send rhizome sync packets when we have neighbours

This commit is contained in:
Jeremy Lakeman 2014-05-29 16:41:27 +09:30
parent 0c294079af
commit b6de3fcd34
17 changed files with 138 additions and 252 deletions

View File

@ -806,13 +806,10 @@ int app_dna_lookup(const struct cli_parsed *parsed, struct cli_context *context)
}
}
else WHYF("packettype=0x%x",rx.packetTypeAndFlags);
if (servalShutdown) break;
}
}
if (servalShutdown) break;
short_timeout=125-(gettime_ms()-now);
}
if (servalShutdown) break;
}
overlay_mdp_client_close(mdp_sockfd);
@ -860,8 +857,6 @@ int app_server_start(const struct cli_parsed *parsed, struct cli_context *contex
INFOF("Starting background server %s", execpath ? execpath : "without exec");
/* Start the Serval process. All server settings will be read by the server process from the
instance directory when it starts up. */
if (server_remove_stopfile() == -1)
RETURN(-1);
// Open the keyring and ensure it contains at least one unlocked identity.
keyring = keyring_open_instance_cli(parsed);
if (!keyring)
@ -1013,9 +1008,6 @@ int app_server_stop(const struct cli_parsed *parsed, struct cli_context *context
return 253;
}
++tries;
/* Create the stopfile, which causes the server process's signal handler to exit
instead of restarting. */
server_create_stopfile();
if (kill(pid, SIGHUP) == -1) {
// ESRCH means process is gone, possibly we are racing with another stop, or servald just
// died voluntarily.
@ -1033,7 +1025,6 @@ int app_server_stop(const struct cli_parsed *parsed, struct cli_context *context
sleep_ms(200); // 5 Hz
while ((running = server_pid()) == pid && gettime_ms() < timeout);
}
server_remove_stopfile();
cli_field_name(context, "tries", ":");
cli_put_long(context, tries, "\n");
return 0;

View File

@ -93,6 +93,26 @@ struct sched_ent{
#define STRUCT_SCHED_ENT_UNUSED {.poll={.fd=-1}, ._poll_index=-1,}
#define DECLARE_ALARM(X) \
extern struct sched_ent _sched_##X; \
void X(struct sched_ent *);
#define DEFINE_ALARM(X) \
void X(struct sched_ent *); \
struct profile_total _stats_##X = {.name=#X,}; \
struct sched_ent _sched_##X = { \
.stats = &_stats_##X, \
.function=X, \
};
#define RESCHEDULE_ALARM(X, A, D) \
do{\
unschedule(&_sched_##X); \
_sched_##X.alarm=(A); \
_sched_##X.deadline=_sched_##X.alarm+(D); \
schedule(&_sched_##X); \
}while(0)
int is_scheduled(const struct sched_ent *alarm);
int is_watching(struct sched_ent *alarm);
int _schedule(struct __sourceloc, struct sched_ent *alarm);
@ -123,6 +143,7 @@ unsigned fd_depth();
#define RETURNNULL(X) do { X; OUT(); return (NULL); } while (0)
#define RETURNVOID do { OUT(); return; } while (0)
DECLARE_ALARM(fd_periodicstats);
void list_alarms();
#endif // __SERVAL_DNA__FDQUEUE_H

View File

@ -10,6 +10,7 @@ HDRS= fifo.h \
meshms.h \
serval_types.h \
serval.h \
server.h \
keyring.h \
socket.h \
cli.h \

View File

@ -75,6 +75,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf.h"
#include "keyring.h"
#include "overlay_interface.h"
#include "server.h"
int overlayMode=0;
@ -104,67 +105,35 @@ int overlayServerMode()
overlay_queue_init();
/* Get the set of socket file descriptors we need to monitor.
Note that end-of-file will trigger select(), so we cannot run select() if we
have any dummy interfaces running. So we do an ugly hack of just waiting no more than
5ms between checks if we have a dummy interface running. This is a reasonable simulation
of wifi latency anyway, so we'll live with it. Larger values will affect voice transport,
and smaller values would affect CPU and energy use, and make the simulation less realistic. */
#define SCHEDULE(X, Y, D) { \
static struct profile_total _stats_##X = {.name="" #X "",}; \
static struct sched_ent _sched_##X = { \
.stats = &_stats_##X, \
.function=X, \
}; \
_sched_##X.alarm = gettime_ms() + (Y);\
_sched_##X.deadline = _sched_##X.alarm + (D);\
schedule(&_sched_##X); \
}
/* Periodically check for server shut down */
SCHEDULE(server_shutdown_check, 0, 100);
/* Periodically reload configuration */
SCHEDULE(server_config_reload, config.server.config_reload_interval_ms, 100);
overlay_mdp_bind_internal_services();
olsr_init_socket();
/* Get rhizome server started BEFORE populating fd list so that
the server's listen socket is in the list for poll() */
if (is_rhizome_enabled()){
rhizome_opendb();
if (config.rhizome.clean_on_start && !config.rhizome.clean_on_open)
rhizome_cleanup(NULL);
}
// start the dna helper if configured
dna_helper_start();
time_ms_t now = gettime_ms();
// preload directory service information
directory_service_init();
/* Periodically check for server shut down */
RESCHEDULE_ALARM(server_shutdown_check, now, 100);
/* Periodically check for new interfaces */
SCHEDULE(overlay_interface_discover, 1, 100);
/* Periodically reload configuration */
RESCHEDULE_ALARM(server_config_reload, now+config.server.config_reload_interval_ms, 100);
overlay_mdp_bind_internal_services();
olsr_init_socket();
/* Periodically advertise bundles */
SCHEDULE(overlay_rhizome_advertise, 1000, 10000);
/* Calculate (and possibly show) CPU usage stats periodically */
SCHEDULE(fd_periodicstats, 3000, 500);
/* Invoke the watchdog executable periodically */
SCHEDULE(server_watchdog, config.server.watchdog.interval_ms, 100);
#undef SCHEDULE
RESCHEDULE_ALARM(fd_periodicstats, now+3000, 500);
cf_on_config_change();
// log message used by tests to wait for the server to start
INFO("Server initialised, entering main loop");
/* Check for activitiy and respond to it */
while(fd_poll());
while(fd_poll() && (serverMode==1));
serverCleanUp();
RETURN(0);
OUT();
}

View File

@ -1020,7 +1020,9 @@ overlay_interface_register(char *name,
return 0;
}
// poll the OS's network interfaces
DEFINE_ALARM(overlay_interface_discover);
void overlay_interface_discover(struct sched_ent *alarm)
{
/* Mark all UP interfaces as DETECTING, so we can tell which interfaces are new, and which are dead */

View File

@ -148,8 +148,8 @@ struct network_destination * add_destination_ref(struct network_destination *ref
void release_destination_ref(struct network_destination *ref);
int set_destination_ref(struct network_destination **ptr, struct network_destination *ref);
DECLARE_ALARM(overlay_interface_discover);
void overlay_interface_discover(struct sched_ent *alarm);
int overlay_interface_register(char *name,
struct socket_address *addr,
struct socket_address *broadcast);

View File

@ -63,6 +63,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "crypto.h"
#include "keyring.h"
#include "socket.h"
#include "server.h"
static void overlay_mdp_poll(struct sched_ent *alarm);
static void mdp_poll2(struct sched_ent *alarm);

View File

@ -207,6 +207,7 @@ int fd_showstats()
return 0;
}
DEFINE_ALARM(fd_periodicstats);
void fd_periodicstats(struct sched_ent *alarm)
{
fd_showstats();

View File

@ -848,7 +848,6 @@ int rhizome_cache_close();
int rhizome_database_filehash_from_id(const rhizome_bid_t *bidp, uint64_t version, rhizome_filehash_t *hashp);
int overlay_mdp_service_rhizome_sync(struct internal_mdp_header *header, struct overlay_buffer *payload);
int rhizome_sync_announce();
int rhizome_sync_bundle_inserted(const unsigned char *bar);
#endif //__SERVAL_DNA__RHIZOME_H

View File

@ -29,6 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf_helpers.h"
#include "str.h"
#include "keyring.h"
#include "server.h"
static int rhizome_delete_manifest_retry(sqlite_retry_state *retry, const rhizome_bid_t *bidp);
static int rhizome_delete_file_retry(sqlite_retry_state *retry, const rhizome_filehash_t *hashp);
@ -1529,7 +1530,7 @@ int rhizome_store_manifest(rhizome_manifest *m)
);
monitor_announce_bundle(m);
if (serverMode)
rhizome_sync_announce();
RESCHEDULE_ALARM(rhizome_sync_announce, gettime_ms(), 10000);
return 0;
}
rollback:

View File

@ -138,113 +138,6 @@ uint64_t rhizome_bar_bidprefix_ll(const unsigned char *bar)
return bidprefix;
}
static int append_bars(struct overlay_buffer *e, sqlite_retry_state *retry, const char *sql, int64_t *last_rowid)
{
sqlite3_stmt *statement = sqlite_prepare(retry, sql);
if (statement == NULL)
return -1;
int params = sqlite3_bind_parameter_count(statement);
switch (params) {
case 0: break;
case 1:
if (sqlite_bind(retry, statement, INT64, *last_rowid, END) == -1)
return -1;
break;
default:
return WHYF("query has invalid number of parameters (%d): %s", params, sqlite3_sql(statement));
}
int count = 0;
while(sqlite_step_retry(retry, statement) == SQLITE_ROW) {
count++;
if (sqlite3_column_type(statement, 0)!=SQLITE_BLOB)
continue;
const void *data = sqlite3_column_blob(statement, 0);
int blob_bytes = sqlite3_column_bytes(statement, 0);
int64_t rowid = sqlite3_column_int64(statement, 1);
if (blob_bytes!=RHIZOME_BAR_BYTES) {
if (config.debug.rhizome_ads)
DEBUG("Found a BAR that is the wrong size - ignoring");
continue;
}
if (ob_remaining(e) < RHIZOME_BAR_BYTES) {
// out of room
count--;
break;
}
ob_append_bytes(e, (unsigned char *)data, RHIZOME_BAR_BYTES);
*last_rowid=rowid;
}
if (statement)
sqlite3_finalize(statement);
return count;
}
/* Periodically queue BAR advertisements
Always advertise the most recent 3 manifests in the table, cycle through the rest of the table, adding 17 BAR's at a time
*/
static uint64_t bundles_available=0;
void overlay_rhizome_advertise(struct sched_ent *alarm)
{
bundles_available=0;
static int64_t bundle_last_rowid=INT64_MAX;
if (!is_rhizome_advertise_enabled())
return;
// TODO deprecate the below announcement method and move this alarm to rhizome_sync.c
rhizome_sync_announce();
int (*oldfunc)() = sqlite_set_tracefunc(is_debug_rhizome_ads);
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
// TODO: DEPRECATE REST OF THIS CODE WHICH SEEMS TO BE CAUSING TOO MUCH CHATTER
// ESPECIALLY FOR PACKET-RADIO
goto end;
/* Get number of bundles available */
if (sqlite_exec_uint64_retry(&retry, &bundles_available, "SELECT COUNT(BAR) FROM MANIFESTS;", END) != 1){
WHY("Could not count BARs for advertisement");
goto end;
}
if (bundles_available<1)
goto end;
struct overlay_frame *frame = malloc(sizeof(struct overlay_frame));
bzero(frame,sizeof(struct overlay_frame));
frame->type = OF_TYPE_RHIZOME_ADVERT;
frame->source = my_subscriber;
frame->ttl = 1;
frame->queue = OQ_OPPORTUNISTIC;
if ((frame->payload = ob_new()) == NULL) {
op_free(frame);
goto end;
}
ob_limitsize(frame->payload, 800);
ob_append_byte(frame->payload, 2);
ob_append_ui16(frame->payload, httpd_server_port);
int64_t rowid=0;
int count = append_bars(frame->payload, &retry,
"SELECT BAR,ROWID FROM MANIFESTS ORDER BY ROWID DESC LIMIT 3",
&rowid);
if (count>=3){
if (bundle_last_rowid>rowid || bundle_last_rowid<=0)
bundle_last_rowid=rowid;
count = append_bars(frame->payload, &retry,
"SELECT BAR,ROWID FROM MANIFESTS WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 17",
&bundle_last_rowid);
if (count<17)
bundle_last_rowid=INT64_MAX;
}
if (overlay_payload_enqueue(frame) == -1)
op_free(frame);
end:
sqlite_set_tracefunc(oldfunc);
alarm->alarm = gettime_ms()+config.rhizome.advertise.interval;
alarm->deadline = alarm->alarm+10000;
schedule(alarm);
}
#define HAS_PORT (1<<1)
#define HAS_MANIFESTS (1<<0)

View File

@ -467,14 +467,17 @@ static void sync_send_response(struct subscriber *dest, int forwards, uint64_t t
OUT();
}
int rhizome_sync_announce()
DEFINE_ALARM(rhizome_sync_announce);
void rhizome_sync_announce(struct sched_ent *alarm)
{
if (!config.rhizome.enable)
return 0;
if (!is_rhizome_advertise_enabled())
return;
int (*oldfunc)() = sqlite_set_tracefunc(is_debug_rhizome_ads);
sync_send_response(NULL, 0, HEAD_FLAG, 5);
sqlite_set_tracefunc(oldfunc);
return 0;
alarm->alarm = gettime_ms()+config.rhizome.advertise.interval;
alarm->deadline = alarm->alarm+10000;
schedule(alarm);
}
int overlay_mdp_service_rhizome_sync(struct internal_mdp_header *header, struct overlay_buffer *payload)

View File

@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <assert.h>
#include "serval.h"
#include "overlay_address.h"
#include "overlay_buffer.h"
@ -25,7 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "str.h"
#include "conf.h"
#include "keyring.h"
#include <assert.h>
#include "server.h"
/*
Link state routing;
@ -255,6 +256,16 @@ static struct link_state *get_link_state(struct subscriber *subscriber)
return subscriber->link_state;
}
static void first_neighbour_found(){
// send rhizome sync periodically
RESCHEDULE_ALARM(rhizome_sync_announce, gettime_ms()+1000, 10000);
}
static void last_neighbour_gone(){
// stop trying to sync rhizome
RESCHEDULE_ALARM(rhizome_sync_announce, TIME_MS_NEVER_WILL, 0);
}
static struct neighbour *get_neighbour(struct subscriber *subscriber, char create)
{
struct neighbour *n = neighbours;
@ -264,6 +275,8 @@ static struct neighbour *get_neighbour(struct subscriber *subscriber, char creat
n = n->_next;
}
if (create){
if (!neighbours)
first_neighbour_found();
n = emalloc_zero(sizeof(struct neighbour));
n->subscriber = subscriber;
n->_next = neighbours;
@ -631,6 +644,9 @@ static void clean_neighbours(time_ms_t now)
n_ptr = &n->_next;
}
}
if (!neighbours)
last_neighbour_gone();
}
static void link_status_html(struct strbuf *b, struct subscriber *n, struct link *link)
@ -703,6 +719,9 @@ void link_neighbour_status_html(struct strbuf *b, struct subscriber *neighbour)
strbuf_puts(b, "Not found<br>");
}
int link_has_neighbours(){
return neighbours?1:0;
}
static int send_legacy_self_announce_ack(struct neighbour *neighbour, struct link_in *link, time_ms_t now){
struct overlay_frame *frame=emalloc_zero(sizeof(struct overlay_frame));

View File

@ -155,8 +155,6 @@ extern const char copyright_servald[];
struct cli_parsed;
extern int servalShutdown;
int rhizome_enabled();
int rhizome_http_server_running();
@ -185,10 +183,6 @@ int server(void);
int server_write_pid();
int server_write_proc_state(const char *path, const char *fmt, ...);
int server_get_proc_state(const char *path, char *buff, size_t buff_len);
int server_create_stopfile();
int server_remove_stopfile();
int server_check_stopfile();
void server_watchdog(struct sched_ent *alarm);
void overlay_mdp_clean_socket_files();
void serverCleanUp();
@ -210,7 +204,6 @@ int overlay_frame_append_payload(struct decode_context *context, int encapsulati
int overlay_packet_init_header(int packet_version, int encapsulation,
struct decode_context *context, struct overlay_buffer *buff,
char unicast, char interface, int seq);
void overlay_rhizome_advertise(struct sched_ent *alarm);
void rhizome_sync_status_html(struct strbuf *b, struct subscriber *subscriber);
int rhizome_cache_count();
@ -301,12 +294,9 @@ int parseDnaReply(const char *buf, size_t len, char *token, char *did, char *nam
int overlay_mdp_setup_sockets();
int overlay_packetradio_setup_port(struct overlay_interface *interface);
void server_config_reload(struct sched_ent *alarm);
void server_shutdown_check(struct sched_ent *alarm);
void overlay_mdp_bind_internal_services();
int overlay_send_probe(struct subscriber *peer, struct network_destination *destination, int queue);
int overlay_send_stun_request(struct subscriber *server, struct subscriber *request);
void fd_periodicstats(struct sched_ent *alarm);
void rhizome_check_connections(struct sched_ent *alarm);
int overlay_queue_init();
@ -344,6 +334,7 @@ int link_add_destinations(struct overlay_frame *frame);
void link_neighbour_short_status_html(struct strbuf *b, const char *link_prefix);
void link_neighbour_status_html(struct strbuf *b, struct subscriber *neighbour);
int link_stop_routing(struct subscriber *subscriber);
int link_has_neighbours();
int generate_nonce(unsigned char *nonce,int bytes);

104
server.c
View File

@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <dirent.h>
#include <signal.h>
#include <unistd.h>
@ -31,6 +32,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf_helpers.h"
#include "overlay_interface.h"
#include "overlay_packet.h"
#include "server.h"
#define PROC_SUBDIR "proc"
#define PIDFILE_NAME "servald.pid"
@ -38,8 +40,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
static char pidfile_path[256];
int servalShutdown = 0;
static int server_getpid = 0;
void signal_handler(int signal);
@ -183,6 +183,7 @@ int server_get_proc_state(const char *path, char *buff, size_t buff_len)
/* Called periodically by the server process in its main loop.
*/
DEFINE_ALARM(server_config_reload);
void server_config_reload(struct sched_ent *alarm)
{
switch (cf_reload_strict()) {
@ -215,6 +216,7 @@ void server_config_reload(struct sched_ent *alarm)
/* Called periodically by the server process in its main loop.
*/
DEFINE_ALARM(server_watchdog);
void server_watchdog(struct sched_ent *alarm)
{
if (config.server.watchdog.executable[0]) {
@ -275,22 +277,31 @@ void server_watchdog(struct sched_ent *alarm)
void cf_on_config_change()
{
if (!serverMode)
return;
time_ms_t now = gettime_ms();
dna_helper_start();
directory_service_init();
/* Periodically check for new interfaces */
RESCHEDULE_ALARM(overlay_interface_discover, now, 100);
if (link_has_neighbours())
// send rhizome sync periodically
RESCHEDULE_ALARM(rhizome_sync_announce, now+1000, 10000);
if (config.server.watchdog.executable[0])
RESCHEDULE_ALARM(server_watchdog, now+config.server.watchdog.interval_ms, 100);
}
/* Called periodically by the server process in its main loop.
*/
DEFINE_ALARM(server_shutdown_check);
void server_shutdown_check(struct sched_ent *alarm)
{
if (servalShutdown) {
INFO("Shutdown flag set -- terminating with cleanup");
serverCleanUp();
exit(0);
}
if (server_check_stopfile() == 1) {
INFO("Shutdown file exists -- terminating with cleanup");
serverCleanUp();
exit(0);
}
/* If this server has been supplanted with another or Serval has been uninstalled, then its PID
file will change or be unaccessible. In this case, shut down without all the cleanup.
Perform this check at most once per second. */
@ -310,49 +321,6 @@ void server_shutdown_check(struct sched_ent *alarm)
}
}
int server_create_stopfile()
{
char stopfile[1024];
if (!FORMF_SERVAL_RUN_PATH(stopfile, STOPFILE_NAME))
return -1;
FILE *f;
if ((f = fopen(stopfile, "w")) == NULL) {
WHY_perror("fopen");
return WHYF("Could not create stopfile '%s'", stopfile);
}
fclose(f);
return 0;
}
int server_remove_stopfile()
{
char stopfile[1024];
if (!FORMF_SERVAL_RUN_PATH(stopfile, STOPFILE_NAME))
return -1;
if (unlink(stopfile) == -1) {
if (errno == ENOENT)
return 0;
WHY_perror("unlink");
return WHYF("Could not unlink stopfile '%s'", stopfile);
}
return 1;
}
int server_check_stopfile()
{
char stopfile[1024];
if (!FORMF_SERVAL_RUN_PATH(stopfile, STOPFILE_NAME))
return -1;
int r = access(stopfile, F_OK);
if (r == 0)
return 1;
if (r == -1 && errno == ENOENT)
return 0;
WHY_perror("access");
WHYF("Cannot access stopfile '%s'", stopfile);
return -1;
}
static void clean_proc()
{
char path_buf[400];
@ -387,9 +355,6 @@ void serverCleanUp()
overlay_mdp_clean_socket_files();
clean_proc();
/* Try to remove shutdown and PID files and exit */
server_remove_stopfile();
}
void signal_handler(int signal)
@ -397,19 +362,20 @@ void signal_handler(int signal)
switch (signal) {
case SIGHUP:
case SIGINT:
/* Terminate the server process. The shutting down should be done from the main-line code
rather than here, so we first try to tell the mainline code to do so. If, however, this is
not the first time we have been asked to shut down, then we will do it here. */
server_shutdown_check(NULL);
INFO("Attempting clean shutdown");
servalShutdown = 1;
return;
/* Trigger the server to close gracefully after any current alarm has completed.
If we get a second signal, exit now.
*/
if (serverMode==1){
INFO("Attempting clean shutdown");
serverMode=2;
return;
}
default:
LOGF(LOG_LEVEL_FATAL, "Caught signal %s", alloca_signal_name(signal));
LOGF(LOG_LEVEL_FATAL, "The following clue may help: %s", crash_handler_clue);
dump_stack(LOG_LEVEL_FATAL);
}
LOGF(LOG_LEVEL_FATAL, "Caught signal %s", alloca_signal_name(signal));
LOGF(LOG_LEVEL_FATAL, "The following clue may help: %s", crash_handler_clue);
dump_stack(LOG_LEVEL_FATAL);
serverCleanUp();
exit(0);
}

28
server.h Normal file
View File

@ -0,0 +1,28 @@
/*
Copyright (C) 2012-2014 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SERVAL_DNA__SERVER_H
#define __SERVAL_DNA__SERVER_H
DECLARE_ALARM(server_shutdown_check);
DECLARE_ALARM(server_watchdog);
DECLARE_ALARM(server_config_reload);
DECLARE_ALARM(rhizome_sync_announce);
DECLARE_ALARM(fd_periodicstats);
#endif // __SERVAL_DNA__SERVER_H

View File

@ -56,7 +56,7 @@ has_link() {
}
has_no_link() {
has_link --any $1 || return 0
has_link --any $@ || return 0
return 1
}