fiss

Friedel's Initialization and Service Supervision
Log | Files | Refs | LICENSE

fsvc.c (7819B)


      1 
      2 #include "../fsvs/message.h"
      3 #include "../fsvs/service.h"
      4 #include "config.h"
      5 #include "signame.h"
      6 #include "util.h"
      7 
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <getopt.h>
     11 #include <stdbool.h>
     12 #include <stdint.h>
     13 #include <string.h>
     14 #include <sys/stat.h>
     15 #include <unistd.h>
     16 
     17 
     18 const char* current_prog(void) {
     19 	return "fsvc";
     20 }
     21 
     22 static const char* const command_names[][2] = {
     23 	{ "up", "u" },           // starts the services, pin as started
     24 	{ "down", "d" },         // stops the service, pin as stopped
     25 	{ "once", "o" },         // same as xup
     26 	{ "term", "t" },         // same as down
     27 	{ "kill", "k" },         // sends kill, pin as stopped
     28 	{ "pause", "p" },        // pauses the service
     29 	{ "cont", "c" },         // resumes the service
     30 	{ "reset", "r" },        // resets the service
     31 	{ "alarm", "a" },        // sends alarm
     32 	{ "hup", "h" },          // sends hup
     33 	{ "int", "i" },          // sends interrupt
     34 	{ "quit", "q" },         // sends quit
     35 	{ "1", "1" },            // sends usr1
     36 	{ "2", "2" },            // sends usr2
     37 	{ "usr1", "1" },         // sends usr1
     38 	{ "usr2", "2" },         // sends usr2
     39 	{ "exit", "x" },         // does nothing
     40 	{ "restart", "!du" },    // restarts the service, don't modify pin
     41 	{ "start", "!u" },       // start the service, pin as started, print status
     42 	{ "stop", "!d" },        // stop the service, pin as stopped, print status
     43 	{ "status", "!" },       // print status
     44 	{ "check", "!" },        // print status
     45 	{ "enable", "!.e" },     // enable service
     46 	{ "disable", "!.d" },    // disable service
     47 	{ 0, 0 }
     48 };
     49 
     50 static const struct option long_options[] = {
     51 	{ "version", no_argument, NULL, 'V' },
     52 	{ "wait", no_argument, NULL, 'w' },
     53 	{ 0 }
     54 };
     55 
     56 struct service_decode {
     57 	int     state;
     58 	pid_t   pid;
     59 	time_t  state_change;
     60 	bool    restart;
     61 	bool    once;
     62 	bool    is_depends;
     63 	bool    wants_up;
     64 	int     last_exit;
     65 	int     return_code;
     66 	uint8_t fail_count;
     67 	bool    paused;
     68 	bool    is_terminating;
     69 };
     70 
     71 static void decode(struct service_decode* s, const struct service_serial* buffer) {
     72 	uint64_t tai = ((uint64_t) buffer->status_change[0] << 56) |
     73 	               ((uint64_t) buffer->status_change[1] << 48) |
     74 	               ((uint64_t) buffer->status_change[2] << 40) |
     75 	               ((uint64_t) buffer->status_change[3] << 32) |
     76 	               ((uint64_t) buffer->status_change[4] << 24) |
     77 	               ((uint64_t) buffer->status_change[5] << 16) |
     78 	               ((uint64_t) buffer->status_change[6] << 8) |
     79 	               ((uint64_t) buffer->status_change[7] << 0);
     80 
     81 	s->state_change = tai - 4611686018427387914ULL;
     82 
     83 	s->state          = buffer->state;
     84 	s->return_code    = buffer->return_code;
     85 	s->fail_count     = buffer->fail_count;
     86 	s->is_terminating = (buffer->flags >> 4) & 0x01;
     87 	s->once           = (buffer->flags >> 3) & 0x01;
     88 	s->restart        = (buffer->flags >> 2) & 0x01;
     89 	s->last_exit      = (buffer->flags >> 0) & 0x03;
     90 
     91 	s->pid = (buffer->pid[0] << 0) |
     92 	         (buffer->pid[1] << 8) |
     93 	         (buffer->pid[2] << 16) |
     94 	         (buffer->pid[3] << 24);
     95 
     96 	s->paused   = buffer->paused;
     97 	s->wants_up = buffer->restart == 'u';
     98 
     99 	s->is_depends = s->wants_up != (s->once || s->restart);
    100 }
    101 
    102 static time_t get_mtime(int dir) {
    103 	struct stat st;
    104 	if (fstatat(dir, "supervise/status", &st, 0) == -1)
    105 		return -1;
    106 	return st.st_mtim.tv_sec;
    107 }
    108 
    109 static int handle_command(int dir, char command) {
    110 	// no custom commands defined
    111 
    112 	(void) dir, (void) command;
    113 
    114 	return -1;
    115 }
    116 
    117 static int send_command(int dir, const char* command) {
    118 	int fd;
    119 	if ((fd = openat(dir, "supervise/control", O_WRONLY | O_NONBLOCK)) == -1)
    120 		return -1;
    121 
    122 	for (const char* c = command; *c != '\0'; c++) {
    123 		if (*c == '.') {
    124 			c++;
    125 			if (handle_command(dir, *c) == -1)
    126 				return -1;
    127 		} else {
    128 			if (write(fd, c, 1) == -1)
    129 				break;
    130 		}
    131 	}
    132 	close(fd);
    133 
    134 	return 0;
    135 }
    136 
    137 int status(int dir) {
    138 	int                   fd;
    139 	time_t                timeval;
    140 	const char*           timeunit = "sec";
    141 	struct service_serial buffer;
    142 	struct service_decode s;
    143 
    144 	if ((fd = openat(dir, "supervise/status", O_RDONLY | O_NONBLOCK)) == -1)
    145 		return -1;
    146 
    147 	if (read(fd, &buffer, sizeof(buffer)) == -1) {
    148 		close(fd);
    149 		return -1;
    150 	}
    151 
    152 	close(fd);
    153 
    154 	decode(&s, &buffer);
    155 
    156 	timeval = time(NULL) - s.state_change;
    157 
    158 	if (timeval >= 60) {
    159 		timeval /= 60;
    160 		timeunit = "min";
    161 		if (timeval >= 60) {
    162 			timeval /= 60;
    163 			timeunit = "h";
    164 			if (timeval >= 24) {
    165 				timeval /= 24;
    166 				timeunit = "d";
    167 			}
    168 		}
    169 	}
    170 
    171 	switch (s.state) {
    172 		case STATE_SETUP:
    173 			print("setting up");
    174 			break;
    175 		case STATE_STARTING:
    176 			print("starting as %d", s.pid);
    177 			break;
    178 		case STATE_ACTIVE_FOREGROUND:
    179 			print("active as %d", s.pid);
    180 			break;
    181 		case STATE_ACTIVE_BACKGROUND:
    182 		case STATE_ACTIVE_DUMMY:
    183 			print("active");
    184 			break;
    185 		case STATE_FINISHING:
    186 			print("finishing as %d", s.pid);
    187 			break;
    188 		case STATE_STOPPING:
    189 			print("stopping as %d", s.pid);
    190 			break;
    191 		case STATE_INACTIVE:
    192 			print("inactive");
    193 			break;
    194 		case STATE_ERROR:
    195 			print("dead (error)");
    196 			break;
    197 	}
    198 
    199 	if (s.paused)
    200 		print(" & paused");
    201 
    202 	print(" since %lu%s", timeval, timeunit);
    203 
    204 	if (s.once == S_ONCE)
    205 		print(", started once");
    206 
    207 	if (s.restart)
    208 		print(", should restart");
    209 
    210 	if (s.is_depends)
    211 		print(", started as dependency");
    212 
    213 	if (s.return_code > 0 && s.last_exit == EXIT_NORMAL)
    214 		print(", exited with %d", s.return_code);
    215 
    216 	if (s.return_code > 0 && s.last_exit == EXIT_SIGNALED)
    217 		print(", crashed with SIG%s", sigabbr(s.return_code));
    218 
    219 	if (s.fail_count > 0)
    220 		print(", failed %d times", s.fail_count);
    221 
    222 	print("\n");
    223 
    224 	return 0;
    225 }
    226 
    227 int main(int argc, char** argv) {
    228 	int opt, dir, fd,
    229 	    timeout = SV_STATUS_WAIT;
    230 	time_t mod, start;
    231 
    232 	const char* command = NULL;
    233 	const char* service;
    234 
    235 	while ((opt = getopt_long(argc, argv, ":Vw:", long_options, NULL)) != -1) {
    236 		switch (opt) {
    237 			case 'V':
    238 				// version
    239 				break;
    240 			case 'w':
    241 				timeout = parse_long(optarg, "seconds");
    242 				break;
    243 			default:
    244 			case '?':
    245 				if (optopt)
    246 					fprint(1, "error: invalid option -%c\n", optopt);
    247 				else
    248 					fprint(1, "error: invalid option %s\n", argv[optind - 1]);
    249 				print_usage_exit(PROG_FSVC, 1);
    250 		}
    251 	}
    252 
    253 	argc -= optind, argv += optind;
    254 
    255 	if (argc == 0) {
    256 		fprint(1, "error: command omitted\n");
    257 		print_usage_exit(PROG_FSVC, 1);
    258 	}
    259 	for (const char** ident = (void*) command_names; ident[0] != NULL; ident++) {
    260 		if (streq(ident[0], argv[0])) {
    261 			command = ident[1];
    262 			break;
    263 		}
    264 	}
    265 	if (command == NULL) {
    266 		fprint(1, "error: unknown command '%s'\n", argv[0]);
    267 		print_usage_exit(PROG_FSVC, 1);
    268 	}
    269 
    270 	argc--, argv++;
    271 
    272 	if (argc == 0) {
    273 		fprint(1, "error: at least one service must be specified\n");
    274 		print_usage_exit(PROG_FSVC, 1);
    275 	}
    276 
    277 	chdir(SV_SERVICE_DIR);
    278 
    279 	bool print_status;
    280 	if ((print_status = command[0] == '!')) {
    281 		command++;
    282 	}
    283 
    284 	for (int i = 0; i < argc; i++) {
    285 		service = progname(argv[i]);
    286 
    287 		if ((dir = open(argv[i], O_DIRECTORY)) == -1) {
    288 			fprint(1, "error: %s: cannot open directory: %s\n", argv[i], strerror(errno));
    289 			continue;
    290 		}
    291 
    292 		if ((fd = openat(dir, "supervise/ok", O_WRONLY | O_NONBLOCK)) == -1) {
    293 			fprint(1, "error: %s: cannot open supervise/control: %s\n", argv[i], strerror(errno));
    294 			continue;
    295 		}
    296 		close(fd);
    297 
    298 		if ((mod = get_mtime(dir)) == -1) {
    299 			fprint(1, "error: %s: cannot get modify-time\n", argv[i]);
    300 			continue;
    301 		}
    302 
    303 		if (command[0] != '\0') {
    304 			if (send_command(dir, command) == -1) {
    305 				fprint(1, "error: %s: unable to send command\n", argv[i]);
    306 				continue;
    307 			}
    308 		} else {
    309 			mod++;    // avoid modtime timeout
    310 		}
    311 
    312 		start = time(NULL);
    313 		if (print_status) {
    314 			while (get_mtime(dir) == mod && time(NULL) - start < timeout)
    315 				usleep(500);    // sleep half a secound
    316 
    317 			if (get_mtime(dir) == mod)
    318 				print("timeout: ");
    319 
    320 			print("%s: ", service);
    321 
    322 			if (status(dir) == -1)
    323 				print("unable to access supervise/status\n");
    324 		}
    325 	}
    326 }