#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "btime_int.h" static void (*btimed_error)(int level, const char *fmt, ...); static void btimed(void); static unsigned int get_btime(void); static int daemon_init(void); static void btimed_perror(int level, const char *fmt, ...); static void sigchld_hdlr(int sig); static void usage(const char *progname); int main(int argc, char **argv) { struct sigaction sa; int debug = 0; int c; while ((c = getopt(argc, argv, "hd")) != -1) { switch (c) { case 'd': debug = 1; break; case 'h': case '?': default: usage(argv[0]); exit(2); } } memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigchld_hdlr; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; sigaction(SIGCHLD, &sa, NULL); if (debug) { btimed_error = btimed_perror; } else { btimed_error = syslog; daemon_init(); } btimed(); return 0; } /* *--------------------------------------------------------------------------- * * btimed -- * * The main loop of the deamon. It listens on its well know port for * messages, responds to the request via a child process. * * Returns: * Never returns. * * Side effects: * Child processes are created. *--------------------------------------------------------------------------- */ static void btimed(void) { int sd; char msg[BTIME_MSGLEN]; struct sockaddr_in cli_addr; int cli_addr_len; struct sockaddr_in my_addr; pid_t pid; ssize_t nbytes; if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { btimed_error(LOG_ERR, "Could not create socket: %s\n", strerror(errno)); exit(1); } memset(&my_addr, 0, sizeof my_addr); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = htonl(INADDR_ANY); my_addr.sin_port = htons(BTIME_PORT); if (bind(sd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) { btimed_error(LOG_ERR, "Could not bind to local address: %s\n", strerror(errno)); exit(1); } for (;;) { memset(&cli_addr, 0, sizeof cli_addr); cli_addr_len = sizeof cli_addr; nbytes = recvfrom(sd, &msg, BTIME_MSGLEN, MSG_WAITALL, (struct sockaddr *)&cli_addr, &cli_addr_len); if ((pid = fork()) < 0) { btimed_error(LOG_ERR, "Could not fork(): %s\n", strerror(errno)); exit(1); } if (pid == 0) { memset(msg, 0, BTIME_MSGLEN); sprintf(msg, "%u\n", get_btime()); sendto(sd, &msg, BTIME_MSGLEN, MSG_DONTWAIT, (struct sockaddr *)&cli_addr, cli_addr_len); exit(0); } } close(sd); } /* *--------------------------------------------------------------------------- * * get_btime -- * * Return machine's boot time. * * Returns: * 0 on failure * non-zero on success. * *--------------------------------------------------------------------------- */ static unsigned int get_btime(void) { FILE *statf; char line[1024]; unsigned int btime = 0; if ((statf = fopen("/proc/stat", "r")) == NULL) { btimed_error(LOG_ERR, "/proc/stat open failure: %s\n", strerror(errno)); exit(1); } while (fgets(line, 1024, statf) != NULL) { if (strstr(line, "btime") != NULL) { sscanf(line, "%*s%u", &btime); } } fclose(statf); return btime; } /* *--------------------------------------------------------------------------- * * daemon_init -- * * Set up a basic daemon environment * * Returns: * 0 on success * non-zero on failure. * * Side effects: * All fds are closed and process is now a session leader. * *--------------------------------------------------------------------------- */ static int daemon_init(void) { pid_t pid; int fd; int maxfd = sysconf(_SC_OPEN_MAX); if ((pid = fork()) < 0) { return(-1); } else if (pid != 0) { exit(0); /* Exit in parent */ } /* * Child sets up daemon environment */ setsid(); /* Become session leader */ chdir("/"); umask(0); for (fd = 0; fd < maxfd; fd++) { close(fd); } errno = 0; /* probably set to EBADFD from above loop, reset */ openlog("btimed", LOG_PID, LOG_USER); return 0; } /* *--------------------------------------------------------------------------- * * btimed_perror -- * * Print error messages to stderr, level is not used, it is there * to have the same calling args as syslog(3) so we can use either * based on debug level. * * Returns: * NA * * Side effects: * None *--------------------------------------------------------------------------- */ static void btimed_perror(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } static void sigchld_hdlr(int sig) { int status; wait(&status); return; } static void usage(const char *progname) { fprintf(stderr, "Usage: %s [OPTION]...\n" "-h\tPrint this help and exit.\n" "-d\tDon't daemonize, send output to stderr.\n", progname); return; }