/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include "daemonize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cppcms { namespace impl { int daemonizer::global_urandom_fd = -1; daemonizer::daemonizer(json::value const &conf) { real_pid = getpid(); try { daemonize(conf); } catch(...) { cleanup(); throw; } } daemonizer::~daemonizer() { cleanup(); } void daemonizer::cleanup() { if(!unlink_file.empty() && real_pid == getpid()) unlink(unlink_file.c_str()); } void daemonizer::daemonize(json::value const &conf) { if(getppid() == 1) return; if(!conf.get("daemon.enable",false)) return; int gid = -1; int uid = -1; int devnull_fd = -1; int lock_fd = -1; try { devnull_fd = open("/dev/null",O_RDWR); if(devnull_fd < 0) { int err = errno; throw cppcms_error(err,"Failed to open /dev/null"); } std::string group = conf.get("daemon.group",""); if(!group.empty()) { struct group *gr = getgrnam(group.c_str()); if(!gr) throw cppcms_error("Invalid group " + group); gid = gr->gr_gid; } std::string user = conf.get("daemon.user",""); if(!user.empty()) { struct passwd *pw = getpwnam(user.c_str()); if(!pw) throw cppcms_error("Invalid user " + user); uid = pw->pw_uid; if(gid==-1) gid = pw->pw_gid; } std::string chroot_dir = conf.get("daemon.chroot",""); if(!chroot_dir.empty()) { global_urandom_fd = open("/dev/urandom",O_RDONLY); if(global_urandom_fd < 0) { int err = errno; throw cppcms_error(err,"Failed to open /dev/urandom"); } if(chdir(chroot_dir.c_str()) < 0) { int err = errno; throw cppcms_error(err,"Failed to chdir to " + chroot_dir); } if(chroot(chroot_dir.c_str()) < 0) { int err = errno; throw cppcms_error(err,"Failed to chroot to " + chroot_dir); } } int fd_limit = conf.get("daemon.fdlimit",-1); if(fd_limit != -1) { struct rlimit lm; lm.rlim_cur = fd_limit; lm.rlim_max = fd_limit; if(setrlimit(RLIMIT_NOFILE,&lm) < 0) { int err = errno; throw cppcms_error(err,"Failed to change daemon.fdlimit"); } } if(gid!=-1) { if(setgid(gid) < 0) { int err = errno; throw cppcms_error(err,"Failed setgid to group " + group); } } if(uid!=-1) { if(setuid(uid) < 0) { int err = errno; throw cppcms_error(err,"Failed setuid to user " + user); } } std::string lock_file = conf.get("daemon.lock",""); if(!lock_file.empty()) { if(lock_file[0]!='/') { char *wd = 0; std::vector buf(256); while((wd=getcwd(&buf.front(),buf.size()))==0 && errno==ERANGE) buf.resize(buf.size()*2); if(!wd) { int err=errno; throw cppcms_error(err,"getcwd failed"); } lock_file=wd + ("/" + lock_file); } lock_fd = ::open(lock_file.c_str(),O_RDWR | O_CREAT | O_EXCL,0666); if(lock_fd < 0) { int err = errno; throw cppcms_error(err,"Failed to create a lock file"); } unlink_file = lock_file; } // forking twice int chid = fork(); if(chid < 0) { int err = errno; throw cppcms_error(err,"Fork failed"); } if(chid != 0) exit(0); real_pid = getpid(); if(setsid() < 0) { int err = errno; throw cppcms_error(err,"setsid failed"); } chid = fork(); if(chid < 0) { int err = errno; throw cppcms_error(err,"2nd fork failed"); } if(chid != 0) exit(0); real_pid = getpid(); dup2(devnull_fd,0); dup2(devnull_fd,1); dup2(devnull_fd,2); close(devnull_fd); devnull_fd = -1; int chdir_r = chdir("/"); (void)(chdir_r); // shut up GCC if(lock_fd!=-1) { std::ostringstream ss; ss.imbue(std::locale::classic()); ss << real_pid; std::string pid = ss.str(); int v = write(lock_fd,pid.c_str(),pid.size()); if(v!=int(pid.size())) { throw cppcms_error("Failed to write to lock file"); } close(lock_fd); lock_fd = -1; } } catch(...) { if(lock_fd >= 0) close(lock_fd); if(devnull_fd >=0) close(devnull_fd); if(global_urandom_fd >= 0) close(global_urandom_fd); throw; } } } // impl } // cppcms