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 }