/* SPDX-License-Identifier: ISC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "syslogd.h" static const struct option long_opts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { "rotate-replace", no_argument, NULL, 'r' }, { "chroot", no_argument, NULL, 'c' }, { "max-size", required_argument, NULL, 'm' }, { "user", required_argument, NULL, 'u' }, { "group", required_argument, NULL, 'g' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "hVcrm:u:g:"; const char *usage_string = "Usage: usyslogd [OPTIONS..]\n\n" "The following options are supported:\n" " -h, --help Print this help text and exit\n" " -V, --version Print version information and exit\n" " -r, --rotate-replace Replace old log files when doing log rotation.\n" " -m, --max-size Automatically rotate log files bigger than this.\n" " -u, --user Run the syslog daemon as this user. If not set,\n" " try to use the user '" DEFAULT_USER "'.\n" " -g, --group Run the syslog daemon as this group. If not set,\n" " try to use the group '" DEFAULT_GROUP "'.\n" " -c, --chroot If set, do a chroot into the log file path.\n"; static volatile sig_atomic_t syslog_run = 1; static volatile sig_atomic_t syslog_rotate = 0; static int log_flags = 0; static size_t max_size = 0; static uid_t uid = 0; static gid_t gid = 0; static bool dochroot = false; static void sighandler(int signo) { switch (signo) { case SIGINT: case SIGTERM: syslog_run = 0; break; case SIGHUP: syslog_rotate = 1; break; default: break; } } static void signal_setup(void) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = sighandler; sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); } static int handle_data(int fd) { char buffer[2048]; syslog_msg_t msg; ssize_t ret; memset(buffer, 0, sizeof(buffer)); ret = read(fd, buffer, sizeof(buffer)); if (ret <= 0) return -1; if (syslog_msg_parse(&msg, buffer)) return -1; return logmgr->write(logmgr, &msg); } static const char *version_string = "usyslogd (usyslog) " PACKAGE_VERSION "\n" "Copyright (C) 2018 David Oberhollenzer\n\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"; static void process_options(int argc, char **argv) { struct passwd *pw = getpwnam(DEFAULT_USER); struct group *grp = getgrnam(DEFAULT_GROUP); char *end; int i; if (pw != NULL) uid = pw->pw_uid; if (grp != NULL) gid = grp->gr_gid; for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case 'r': log_flags |= LOG_ROTATE_OVERWRITE; break; case 'm': log_flags |= LOG_ROTATE_SIZE_LIMIT; max_size = strtol(optarg, &end, 10); if (max_size == 0 || *end != '\0') { fputs("Numeric argument > 0 expected for -m\n", stderr); goto fail; } break; case 'u': pw = getpwnam(optarg); if (pw == NULL) { fprintf(stderr, "Cannot get UID for user %s\n", optarg); goto fail; } uid = pw->pw_uid; break; case 'g': grp = getgrnam(optarg); if (grp == NULL) { fprintf(stderr, "Cannot get GID for group %s\n", optarg); goto fail; } gid = grp->gr_gid; break; case 'c': dochroot = true; break; case 'h': fputs(usage_string, stdout); exit(EXIT_SUCCESS); case 'V': fputs(version_string, stdout); exit(EXIT_SUCCESS); default: goto fail; } } return; fail: fputs("Try `usyslogd --help' for more information\n", stderr); exit(EXIT_FAILURE); } static int chroot_setup(void) { size_t i, len = strlen(SYSLOG_PATH); char *buffer = alloca(len + 1); memcpy(buffer, SYSLOG_PATH, len + 1); for (i = 0; i <= len && buffer[i] == '/'; ++i) ; for (; i <= len; ++i) { if (buffer[i] == '\0' || buffer[i] == '/') { buffer[i] = '\0'; if (mkdir(buffer, 0755)) { if (errno != EEXIST) { perror(buffer); return -1; } } if (i < (len - 1)) buffer[i] = '/'; } } if (uid > 0 && gid > 0 && chown(SYSLOG_PATH, uid, gid) != 0) { perror("chown " SYSLOG_PATH); return -1; } if (chmod(SYSLOG_PATH, 0750)) { perror("chmod " SYSLOG_PATH); return -1; } if (chdir(SYSLOG_PATH)) { perror("cd " SYSLOG_PATH); return -1; } if (dochroot && chroot(SYSLOG_PATH) != 0) { perror("chroot " SYSLOG_PATH); return -1; } return 0; } static int user_setup(void) { if (gid > 0 && setresgid(gid, gid, gid) != 0) { perror("setgid"); return -1; } if (uid > 0 && setresuid(uid, uid, uid) != 0) { perror("setuid"); return -1; } return 0; } int main(int argc, char **argv) { int sfd, status = EXIT_FAILURE; process_options(argc, argv); signal_setup(); sfd = mksock(SYSLOG_SOCKET); if (sfd < 0) return EXIT_FAILURE; if (uid > 0 && gid > 0 && chown(SYSLOG_SOCKET, uid, gid) != 0) { perror("chown " SYSLOG_SOCKET); return -1; } if (chroot_setup()) return EXIT_FAILURE; if (user_setup()) return EXIT_FAILURE; if (logmgr->init(logmgr, log_flags, max_size)) goto out; while (syslog_run) { if (syslog_rotate) { logmgr->rotate(logmgr); syslog_rotate = 0; } handle_data(sfd); } status = EXIT_SUCCESS; out: logmgr->cleanup(logmgr); if (sfd > 0) close(sfd); unlink(SYSLOG_SOCKET); return status; }