Rewrite server start/status/stop commands

Now correctly probes for server process existence using kill(pid, 0)
Status command does not dump config (use "config get" instead)
Output uses JNI cli output fields
Stop command uses 5 Hz wakeup, not busy wait while server exits
This commit is contained in:
Andrew Bettison 2012-05-07 13:49:38 +09:30
parent 4ae3c95992
commit cbc367b1f5
6 changed files with 150 additions and 162 deletions

View File

@ -33,39 +33,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "serval.h"
#include "rhizome.h"
static int servalNodeRunning(int *pid)
/** Return the PID of the currently running server process, return 0 if there is none.
*/
static int servalNodeRunning()
{
const char *instancepath = serval_instancepath();
struct stat st;
int r=stat(instancepath,&st);
if (r)
return setReason("Instance path '%s' non existant or not accessable: %s [errno=%d]"
if (stat(instancepath, &st) == -1)
return setReason(
"Instance path '%s' non existant or not accessable: %s [errno=%d]"
" (Set SERVALINSTANCE_PATH to specify an alternate location)",
instancepath, strerror(errno), errno
);
if ((st.st_mode&S_IFMT)!=S_IFDIR)
if ((st.st_mode & S_IFMT) != S_IFDIR)
return setReason("Instance path '%s' is not a directory", instancepath);
int running=0;
char filename[1024];
if (FORM_SERVAL_INSTANCE_PATH(filename, "serval.pid")) {
FILE *f=fopen(filename,"r");
if (f) {
char line[1024];
line[0]=0; fgets(line,1024,f);
*pid = strtoll(line,NULL,10);
running=*pid;
if (running) {
/* Check that process is really running.
Some systems don't have /proc (including mac),
so we need to find out some otherway.*/
running=1; // assume pid means is running for now
}
fclose(f);
}
if (!FORM_SERVAL_INSTANCE_PATH(filename, "serval.pid"))
return -1;
FILE *f = NULL;
if ((f = fopen(filename, "r"))) {
char buf[20];
fgets(buf, sizeof buf, f);
fclose(f);
int pid = atoi(buf);
if (pid > 0 && kill(pid, 0) != -1)
return pid;
unlink(filename);
}
return running;
return 0;
}
int cli_usage() {
@ -582,137 +577,122 @@ int app_server_start(int argc, const char *const *argv, struct command_line_opti
int foregroundP= (argc >= 2 && !strcasecmp(argv[1], "foreground"));
if (cli_arg(argc, argv, o, "instance path", &thisinstancepath, cli_absolute_path, NULL) == -1)
return -1;
/* Create the instance directory if it does not yet exist */
if (create_serval_instance_dir() == -1)
return -1;
cli_puts("instancepath");
cli_delim(":");
cli_puts(serval_instancepath());
cli_delim("\n");
/* Now that we know our instance path, we can ask for the default set of
network interfaces that we will take interest in. */
overlay_interface_args(confValueGet("interfaces",""));
if (strlen(confValueGet("interfaces",""))<1) {
WHY("Noone has told me which network interfaces to listen on; "
"you should probably put something in the interfaces setting.");
const char *interfaces = confValueGet("interfaces", "");
if (!interfaces[0])
WHY("No network interfaces configured (empty 'interfaces' config setting)");
overlay_interface_args(interfaces);
int pid = servalNodeRunning();
if (pid < 0)
return -1;
int ret = 1;
if (pid > 0) {
WHYF("Serval process already running (pid=%d)", pid);
} else {
/* Start the Serval process.
All server settings will be read by the server process from the
instance directory when it starts up.
We can just become the server process ourselves --- no need to fork.
*/
rhizome_datastore_path = serval_instancepath();
rhizome_opendb();
overlayMode = 1;
if ((pid = server(NULL, foregroundP)) <= 0)
return -1;
ret = 0;
}
int pid=-1;
int running = servalNodeRunning(&pid);
if (running<0) return -1;
if (running>0)
return WHYF("Serval process already running (pid=%d)", pid);
/* Start the Serval process.
All server settings will be read by the server process from the
instance directory when it starts up.
We can just become the server process ourselves --- no need to fork.
*/
rhizome_datastore_path = serval_instancepath();
rhizome_opendb();
overlayMode=1;
return server(NULL,foregroundP);
cli_puts("pid");
cli_delim(":");
cli_printf("%d", pid);
cli_delim("\n");
return ret;
}
int app_server_stop(int argc, const char *const *argv, struct command_line_option *o)
{
if (cli_arg(argc, argv, o, "instance path", &thisinstancepath, cli_absolute_path, NULL) == -1)
return -1;
int tries=0;
for(tries=0;tries<3;tries++)
{
int pid=-1;
int running = servalNodeRunning(&pid);
if (running>0) {
/* Is running, so we can try to kill it.
This is a little complicated by the fact that we catch most signals
so that unexpected aborts just restart.
What we can do is put some code in the signal handler that does abort
the process if a certain file exists, perhaps instance_path/doshutdown,
and removes the file.
*/
if (pid<0) {
WHY("Could not determine process id of Serval process. Stale instance perhaps?");
return -1;
const char *instancepath = serval_instancepath();
int pid = servalNodeRunning();
if (pid < 0)
return -1;
cli_puts("instancepath");
cli_delim(":");
cli_puts(instancepath);
cli_delim("\n");
if (pid) {
cli_puts("pid");
cli_delim(":");
cli_printf("%d", pid);
cli_delim("\n");
int tries = 0;
while (1) {
if (tries >= 3)
return WHYF(
"Serval process for instance '%s' did not stop after %d SIGHUP signals",
instancepath, tries
);
++tries;
/* Create the stopfile, which causes the server process's signal handler to exit
instead of restarting. */
char stopfile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, "doshutdown"))
return -1;
FILE *f;
if ((f = fopen(stopfile, "w")) == NULL)
return WHYF("Could not create shutdown file '%s'", stopfile);
fclose(f);
if (kill(pid,SIGHUP) == -1) {
// ESRCH means process is gone, possibly we are racing with another stop, or servald just
// died unexpectedly.
if (errno == ESRCH) {
serverCleanUp();
break;
}
char stopfile[1024];
FILE *f;
if (!(FORM_SERVAL_INSTANCE_PATH(stopfile, "doshutdown") && (f = fopen(stopfile, "w")))) {
WHY("Could not create shutdown file");
return -1;
}
fclose(f);
int result=kill(pid,SIGHUP);
if (!result) {
WHY("Stop request sent to Serval process.");
} else {
WHY("Could not send SIGHUP to Serval process.");
switch (errno) {
case EINVAL: WHY("This is embarassing, but the operating system says I don't know how to send a signal."); break;
case EPERM: WHY("I don't have permission to stop the Serval process. You could try using sudo, or run the stop command as the appropriate user."); break;
case ENOENT:
case ESRCH: WHY("The process id I have recorded doesn't seem to exist anymore. Did someone kill the process without telling me?");
/* Clean up any lingering mess */
servalShutdownCleanly();
break;
default:
perror("This is reason given by the operating system");
}
return -1;
}
/* Allow a few seconds for the process to die, and keep an eye on things
while this is happening. */
time_t timeout=time(0)+2;
while(timeout>time(0)) {
pid=-1;
int running = servalNodeRunning(&pid);
if (running<1) {
WHY("Serval process appears to have stopped.");
return 0;
}
}
} else {
return WHY("Serval process for that instance does not appear to be running.");
return WHYF("Error sending SIGHUP to Serval instance '%s' process pid=%d: %s [errno=%d]",
instancepath, pid, strerror(errno), errno
);
}
/* Allow a few seconds for the process to die, and keep an eye on things while this is
happening. */
time_t timeout = time(NULL) + 2;
while (time(NULL) < timeout && servalNodeRunning() == pid)
usleep(200000); // 5 Hz
}
return WHY("Tried to stop servald without success");
cli_puts("tries");
cli_delim(":");
cli_printf("%d", tries);
cli_delim("\n");
}
return pid ? 0 : 1;
}
int app_server_status(int argc, const char *const *argv, struct command_line_option *o)
{
if (cli_arg(argc, argv, o, "instance path", &thisinstancepath, cli_absolute_path, NULL) == -1)
return -1;
/* Display configuration information */
char filename[1024];
FILE *f;
if (FORM_SERVAL_INSTANCE_PATH(filename, "serval.conf") && (f = fopen(filename, "r"))) {
char line[1024];
line[0]=0; fgets(line,1024,f);
printf("\nServal Mesh configuration:\n");
while(line[0]) {
printf(" %s",line);
line[0]=0; fgets(line,1024,f);
}
fclose(f);
int pid = servalNodeRunning();
if (pid < 0)
return -1;
cli_puts("instancepath");
cli_delim(":");
cli_puts(serval_instancepath());
cli_delim("\n");
if (pid) {
cli_puts("pid");
cli_delim(":");
cli_printf("%d", pid);
cli_delim("\n");
}
/* Display running status of daemon from serval.pid file */
int pid=-1;
int running = servalNodeRunning(&pid);
if (running<0) return -1;
printf("For Serval Mesh instance %s:\n", serval_instancepath());
if (running)
printf(" Serval mesh process is running (pid=%d)\n",pid);
else
printf(" Serval Mesh process not running\n");
return 0;
return pid > 0 ? 0 : 1;
}
int app_mdp_ping(int argc, const char *const *argv, struct command_line_option *o)

44
dna.c
View File

@ -266,7 +266,7 @@ int form_serval_instance_path(char *buf, size_t bufsiz, const char *path)
{
if (snprintf(buf, bufsiz, "%s/%s", serval_instancepath(), path) < bufsiz)
return 1;
fprintf(stderr, "Cannot form pathname \"%s/%s\" -- buffer too small (%lu bytes)", serval_instancepath(), path, (unsigned long)bufsiz);
setReason("Cannot form pathname \"%s/%s\" -- buffer too small (%lu bytes)", serval_instancepath(), path, (unsigned long)bufsiz);
return 0;
}
@ -290,9 +290,8 @@ int create_serval_instance_dir() {
return 0;
}
void servalShutdownCleanly()
void serverCleanUp()
{
WHY("Shutting down as requested.");
/* Try to remove shutdown and PID files and exit */
char filename[1024];
if (FORM_SERVAL_INSTANCE_PATH(filename, "doshutdown")) {
@ -308,26 +307,29 @@ void servalShutdownCleanly()
} else {
overlay_mdp_client_done();
}
exit(0);
}
void signal_handler( int signal ) {
if (signal==SIGQUIT) servalShutdownCleanly();
if (signal==SIGHUP||signal==SIGINT) {
/* Shut down.
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. */
if (servalShutdown) {
/* We have been asked before, so shut down cleanly */
servalShutdownCleanly();
} else {
WHY("Asking Serval process to shutdown cleanly");
servalShutdown=1;
}
return;
switch (signal) {
case SIGQUIT:
serverCleanUp();
exit(0);
case SIGHUP:
case SIGINT:
/* Shut down.
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. */
if (servalShutdown) {
/* We have been asked before, so shut down cleanly */
serverCleanUp();
exit(0);
} else {
WHY("Asking Serval process to shutdown cleanly");
servalShutdown=1;
}
return;
}
/* oops - caught a bad signal -- exec() ourselves fresh */
@ -477,7 +479,7 @@ void signal_handler( int signal ) {
exit(-3);
}
int setVerbosity(char *optarg) {
int setVerbosity(const char *optarg) {
long long old_debug=debug;
debug=strtoll(optarg,NULL,10);
if (strstr(optarg,"interfaces")) debug|=DEBUG_OVERLAYINTERFACES;
@ -507,7 +509,7 @@ int setVerbosity(char *optarg) {
if (strstr(optarg,"queues")) debug|=DEBUG_QUEUES;
if (strstr(optarg,"broadcasts")) debug|=DEBUG_BROADCASTS;
if (old_debug==debug) {
if (old_debug==debug && optarg[0]) {
fprintf(stderr,"WARNING: Option '%s' had no effect on existing debug/verbosity level.\n",
optarg);
}

View File

@ -121,7 +121,10 @@ int overlayServerMode()
while(1) {
if (servalShutdown) servalShutdownCleanly();
if (servalShutdown) {
serverCleanUp();
exit(0);
}
/* Work out how long we can wait before we need to tick */
long long ms=overlay_time_until_next_tick();

View File

@ -149,7 +149,7 @@ int overlay_interface_arg(char *arg)
else { free(r); return WHY("Bad interface specification"); }
}
int overlay_interface_args(char *arg)
int overlay_interface_args(const char *arg)
{
/* Parse series of comma-separated interface definitions from a single argument
*/

View File

@ -381,7 +381,7 @@ extern int hexdigit[16];
extern int sock;
void servalShutdownCleanly();
void serverCleanUp();
char *confValueGet(char *var,char *defaultValue);
int recvwithttl(int sock,unsigned char *buffer,int bufferlen,int *ttl,
struct sockaddr *recvaddr,unsigned int *recvaddrlen);
@ -766,7 +766,7 @@ int overlay_rx_messages();
int overlay_check_ticks();
int overlay_add_selfannouncement();
int overlay_frame_package_fmt1(overlay_frame *p,overlay_buffer *b);
int overlay_interface_args(char *arg);
int overlay_interface_args(const char *arg);
int overlay_get_nexthop(unsigned char *d,unsigned char *nexthop,int *nexthoplen,
int *interface);
int overlay_sendto(struct sockaddr_in *recipientaddr,unsigned char *bytes,int len);
@ -1233,7 +1233,7 @@ typedef struct overlay_mdp_frame {
int keyring_mapping_request(keyring_file *k,overlay_mdp_frame *req);
int setVerbosity(char *optarg);
int setVerbosity(const char *optarg);
/* Client-side MDP function */
extern int mdp_client_socket;

View File

@ -128,12 +128,12 @@ int server(char *backing_file,int foregroundMode)
/* Record PID */
char filename[1024];
if (!FORM_SERVAL_INSTANCE_PATH(filename, "serval.pid"))
exit(-1);
return -1;
FILE *f=fopen(filename,"w");
if (!f) {
WHYF("Could not write to PID file %s", filename);
perror("fopen");
exit(-1);
return -1;
}
fprintf(f,"%d\n",getpid());
fclose(f);
@ -532,7 +532,10 @@ int simpleServerMode()
int len;
int r;
if (servalShutdown) servalShutdownCleanly();
if (servalShutdown) {
serverCleanUp();
exit(0);
}
bzero((void *)&recvaddr,sizeof(recvaddr));