/* * Copyright (c) 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * service.c * Start and stop services. */ #include #include #include #include #include #include #include #include #include #include static int open_local_client_socket(const char* path, int flags) { size_t path_length = strlen(path); size_t addr_size = offsetof(struct sockaddr_un, sun_path) + path_length + 1; struct sockaddr_un* sockaddr = malloc(addr_size); if ( !sockaddr ) return -1; sockaddr->sun_family = AF_LOCAL; strcpy(sockaddr->sun_path, path); int fd = socket(AF_LOCAL, SOCK_STREAM | flags, 0); if ( fd < 0 ) return free(sockaddr), -1; if ( connect(fd, (const struct sockaddr*) sockaddr, addr_size) < 0 ) return close(fd), free(sockaddr), -1; free(sockaddr); return fd; } int main(int argc, char* argv[]) { const char* init_socket = getenv("INIT_SOCKET"); if ( !init_socket ) init_socket = "/var/run/init"; bool raw = false; const char* source = "local"; const struct option longopts[] = { {"source", required_argument, NULL, 't'}, {"raw", no_argument, NULL, 'r'}, {0, 0, 0, 0} }; const char* opts = "rs:"; int opt; while ( (opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1 ) { switch ( opt ) { case 'r': raw = true; break; case 's': source = optarg; break; default: return 2; } } int fd = open_local_client_socket(init_socket, 0); if ( fd < 0 ) err(1, "%s", init_socket); if ( raw ) { for ( int i = optind; i < argc; i++ ) { if ( dprintf(fd, "%s%c", argv[i], i + 1 == argc ? '\n' : ' ') < 0 ) err(1, "%s", init_socket); } return 0; } if ( argc - optind < 2 ) errx(1, "usage: "); const char* daemon = argv[optind++]; const char* command = argv[optind++]; if ( !strcmp(command, "enable") ) { // TODO: Write out new config. if ( dprintf(fd, "require %s %s start\n", source, daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "disable") ) { // TODO: Write out new config. if ( dprintf(fd, "unrequire %s %s\n", source, daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "start") ) { if ( dprintf(fd, "require %s %s start\n", source, daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "stop") ) { if ( dprintf(fd, "unrequire %s %s\n", source, daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "restart") ) { if ( dprintf(fd, "restart %s\n", daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "reload") ) { if ( dprintf(fd, "reload %s\n", daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "reconfigure") ) { if ( dprintf(fd, "reconfigure %s\n", daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "terminate") ) { if ( dprintf(fd, "terminate %s\n", daemon) < 0 ) err(1, "%s", init_socket); } else if ( !strcmp(command, "kill") ) { if ( dprintf(fd, "kill %s\n", daemon) < 0 ) err(1, "%s", init_socket); } // TODO: --list // TODO: status // TODO: signal // TODO: pid // TODO: requirements // TODO: log else errx(1, "unknown command: %s", command); return 0; }