#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "appconfig.h" #include "log.h" #include "web_client.h" #include "plugins_d.h" #include "rrd.h" #include "popen.h" #include "main.h" #include "daemon.h" char pidfile[FILENAME_MAX + 1] = ""; int pidfd = -1; void sig_handler(int signo) { switch(signo) { case SIGILL: case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: case SIGSYS: case SIGTRAP: case SIGXCPU: case SIGXFSZ: infoerr("Death signaled exit (signal %d).", signo); signal(signo, SIG_DFL); break; case SIGKILL: case SIGTERM: case SIGQUIT: case SIGINT: case SIGHUP: case SIGUSR1: case SIGUSR2: infoerr("Signaled exit (signal %d).", signo); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGCHLD, SIG_IGN); netdata_cleanup_and_exit(1); break; case SIGPIPE: infoerr("Signaled PIPE (signal %d).", signo); // this is received when web clients send a reset // no need to log it. // infoerr("Ignoring signal %d.", signo); break; default: info("Signal %d received. Falling back to default action for it.", signo); signal(signo, SIG_DFL); break; } } int become_user(const char *username) { struct passwd *pw = getpwnam(username); if(!pw) { error("User %s is not present.", username); return -1; } uid_t uid = pw->pw_uid; gid_t gid = pw->pw_gid; if(pidfile[0] && getuid() != uid) { // we are dropping privileges if(chown(pidfile, uid, gid) != 0) error("Cannot chown pidfile '%s' to user '%s'", pidfile, username); else if(pidfd != -1) { // not need to keep it open close(pidfd); pidfd = -1; } } else if(pidfd != -1) { // not need to keep it open close(pidfd); pidfd = -1; } if(setresgid(gid, gid, gid) != 0) { error("Cannot switch to user's %s group (gid: %d).", username, gid); return -1; } if(setresuid(uid, uid, uid) != 0) { error("Cannot switch to user %s (uid: %d).", username, uid); return -1; } if(setgid(gid) != 0) { error("Cannot switch to user's %s group (gid: %d).", username, gid); return -1; } if(setegid(gid) != 0) { error("Cannot effectively switch to user's %s group (gid: %d).", username, gid); return -1; } if(setuid(uid) != 0) { error("Cannot switch to user %s (uid: %d).", username, uid); return -1; } if(seteuid(uid) != 0) { error("Cannot effectively switch to user %s (uid: %d).", username, uid); return -1; } return(0); } int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp) { fflush(NULL); // open the files before forking int input_fd = -1, output_fd = -1, error_fd = -1, dev_null; if(input && *input) { if((input_fd = open(input, O_RDONLY, 0666)) == -1) { error("Cannot open input file '%s'.", input); return -1; } } if(output && *output) { if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { error("Cannot open output log file '%s'", output); if(input_fd != -1) close(input_fd); return -1; } } if(error && *error) { if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { error("Cannot open error log file '%s'.", error); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); return -1; } } if(access && *access && access_fd) { if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { error("Cannot open access log file '%s'", access); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); if(error_fd != -1) close(error_fd); return -1; } if(access_fp) { *access_fp = fdopen(*access_fd, "w"); if(!*access_fp) { error("Cannot migrate file's '%s' fd %d.", access, *access_fd); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); if(error_fd != -1) close(error_fd); close(*access_fd); *access_fd = -1; return -1; } } } if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) { perror("Cannot open /dev/null"); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); if(error_fd != -1) close(error_fd); if(access && access_fd && *access_fd != -1) { close(*access_fd); *access_fd = -1; if(access_fp) { fclose(*access_fp); *access_fp = NULL; } } return -1; } // all files opened // lets do it if(!dont_fork) { int i = fork(); if(i == -1) { perror("cannot fork"); exit(1); } if(i != 0) { exit(0); // the parent } // become session leader if (setsid() < 0) { perror("Cannot become session leader."); exit(2); } } signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGWINCH, SIG_IGN); // fork() again if(!dont_fork) { int i = fork(); if(i == -1) { perror("cannot fork"); exit(1); } if(i != 0) { exit(0); // the parent } } // Set new file permissions umask(0); // close all files if(close_all_files) { int i; for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--) if( ((access_fd && i != *access_fd) || !access_fd) && i != dev_null && i != input_fd && i != output_fd && i != error_fd && fd_is_valid(i) ) close(i); } else { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } // put the opened files // to our standard file descriptors if(input_fd != -1) { if(input_fd != STDIN_FILENO) { dup2(input_fd, STDIN_FILENO); close(input_fd); } input_fd = -1; } else dup2(dev_null, STDIN_FILENO); if(output_fd != -1) { if(output_fd != STDOUT_FILENO) { dup2(output_fd, STDOUT_FILENO); close(output_fd); } output_fd = -1; } else dup2(dev_null, STDOUT_FILENO); if(error_fd != -1) { if(error_fd != STDERR_FILENO) { dup2(error_fd, STDERR_FILENO); close(error_fd); } error_fd = -1; } else dup2(dev_null, STDERR_FILENO); // close /dev/null if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO) close(dev_null); // generate our pid file if(pidfile[0]) { pidfd = open(pidfile, O_RDWR | O_CREAT, 0644); if(pidfd >= 0) { if(ftruncate(pidfd, 0) != 0) error("Cannot truncate pidfile '%s'.", pidfile); char b[100]; sprintf(b, "%d\n", getpid()); ssize_t i = write(pidfd, b, strlen(b)); if(i <= 0) error("Cannot write pidfile '%s'.", pidfile); // don't close it, we might need it at exit // close(pidfd); } else error("Failed to open pidfile '%s'.", pidfile); } if(user && *user) { if(become_user(user) != 0) { error("Cannot become user '%s'. Continuing as we are.", user); } else info("Successfully became user '%s'.", user); } else if(pidfd != -1) close(pidfd); return(0); }