/* * Where do we want to mount the controllers? We used to mount * them under a tmpfs under /sys/fs/cgroup, for all to share. Now * we want to have our socket there. So how about /run/cgmanager/fs? * TODO read this from configuration file too * TODO do we want to create these in a tmpfs? */ static bool setup_base_path(void) { base_path = strdup("/run/cgmanager/fs"); if (!base_path) return false; if (mkdir("/run", 0755) < 0 && errno != EEXIST) { nih_fatal("failed to create /run"); return false; } if (mkdir("/run/cgmanager", 0755) < 0 && errno != EEXIST) { nih_fatal("failed to create /run/cgmanager"); return false; } if (mkdir("/run/cgmanager/fs", 0755) < 0 && errno != EEXIST) { nih_fatal("failed to create /run/cgmanager/fs"); return false; } return true; }
/** * nih_error_clear: * * Ensure that the current context has no raised error, if it does then * there's a programming error so we abort after logging where the error * was originally raised. **/ static void nih_error_clear (void) { nih_assert (context_stack != NULL); if (! NIH_UNLIKELY (CURRENT_CONTEXT->error)) return; nih_fatal ("%s:%d: Unhandled error from %s: %s", CURRENT_CONTEXT->error->filename, CURRENT_CONTEXT->error->line, CURRENT_CONTEXT->error->function, CURRENT_CONTEXT->error->message); abort (); }
static void set_clone_children(const char *path) { char p[MAXPATHLEN]; FILE *f; int ret; ret = snprintf(p, MAXPATHLEN, "%s/cgroup.clone_children", path); if (ret < 0 || ret >= MAXPATHLEN) return; f = fopen(p, "w"); if (!f) { nih_fatal("Failed to set clone_children"); return; } fprintf(f, "1\n"); fclose(f); }
static void set_use_hierarchy(const char *path) { char p[MAXPATHLEN]; FILE *f; int ret; ret = snprintf(p, MAXPATHLEN, "%s/memory.use_hierarchy", path); if (ret < 0 || ret >= MAXPATHLEN) return; f = fopen(p, "w"); if (!f) { nih_fatal("Failed to set memory.use_hierarchy"); return; } fprintf(f, "1\n"); fclose(f); }
/** * shutdown_now: * * Send a signal to init to shut down the machine. * * This does not return. **/ static void shutdown_now (void) { nih_local char **extra_env = NULL; NihDBusError * dbus_err; if (init_halt) { char *e; e = NIH_MUST (nih_sprintf (NULL, "INIT_HALT=%s", init_halt)); extra_env = NIH_MUST (nih_str_array_new (NULL)); NIH_MUST (nih_str_array_addp (&extra_env, NULL, NULL, e)); } if (sysv_change_runlevel (runlevel, extra_env, NULL, NULL) < 0) { dbus_err = (NihDBusError *)nih_error_get (); if ((dbus_err->number != NIH_DBUS_ERROR) || strcmp (dbus_err->name, DBUS_ERROR_NO_SERVER)) { nih_fatal ("%s", dbus_err->message); nih_free (dbus_err); exit (1); } nih_free (dbus_err); /* Connection Refused means that init isn't running, this * might mean we've just upgraded to upstart and haven't * yet rebooted ... so try /dev/initctl */ sysvinit_shutdown (); } unlink (ETC_NOLOGIN); nih_main_unlink_pidfile (); exit (0); }
int main (int argc, char *argv[]) { char ** args; int ret; DBusServer * server; struct stat sb; struct rlimit newrlimit; nih_main_init (argv[0]); nih_option_set_synopsis (_("Control group manager")); nih_option_set_help (_("The cgroup manager daemon")); args = nih_option_parser (NULL, argc, argv, options, FALSE); if (! args) exit (1); if (!setup_cgroup_dir()) { nih_fatal("Failed to set up cgmanager socket"); exit(1); } /* Setup the DBus server */ server = nih_dbus_server (CGMANAGER_DBUS_PATH, client_connect, client_disconnect); nih_assert (server != NULL); if (!setup_base_run_path()) { nih_fatal("Error setting up base cgroup path"); return -1; } if (collect_subsystems(extra_cgroup_mounts) < 0) { nih_fatal("failed to collect cgroup subsystems"); exit(1); } if (!create_agent_symlinks()) { nih_fatal("Error creating release agent symlinks"); exit(1); } if (setup_cgroup_mounts() < 0) { nih_fatal ("Failed to set up cgroup mounts"); exit(1); } if (!move_self_to_root()) { nih_fatal ("Failed to move self to root cgroup"); exit(1); } if (stat("/proc/self/ns/pid", &sb) == 0) { mypidns = read_pid_ns_link(getpid()); setns_pid_supported = true; } if (stat("/proc/self/ns/user", &sb) == 0) { myuserns = read_user_ns_link(getpid()); setns_user_supported = true; } newrlimit.rlim_cur = 10000; newrlimit.rlim_max = 10000; if (setrlimit(RLIMIT_NOFILE, &newrlimit) < 0) nih_warn("Failed to increase open file limit: %s", strerror(errno)); /* Become daemon */ if (daemonise) { if (nih_main_daemonise () < 0) { NihError *err; err = nih_error_get (); nih_fatal ("%s: %s", _("Unable to become daemon"), err->message); nih_free (err); exit (1); } } if (sigstop) raise(SIGSTOP); ret = nih_main_loop (); /* Destroy any PID file we may have created */ if (daemonise) { nih_main_unlink_pidfile(); } return ret; }
int main (int argc, char *argv[]) { char **args; int runlevel; int ret; nih_main_init (argv[0]); nih_option_set_usage ("RUNLEVEL"); nih_option_set_synopsis (_("Change runlevel.")); nih_option_set_help ( _("RUNLEVEL should be one of 0123456sS, where s and S are " "considered identical.\n" "\n" "RUNLEVEL may also be Q or q to instruct the init daemon " "to reload its configuration, this is rarely necessary " "since the daemon watches its configuration for changes.\n" "\n" "RUNLEVEL may be U or u to instruct the init daemon to " "re-execute itself, this is not recommended since Upstart " "does not currently preserve its state.\n")); args = nih_option_parser (NULL, argc, argv, options, FALSE); if (! args) exit (1); /* First argument must be a single character we know */ if (! args[0]) { fprintf (stderr, _("%s: missing runlevel\n"), program_name); nih_main_suggest_help (); exit (1); } if ((! strchr ("0123456SsQqUu", args[0][0])) || args[0][1]) { fprintf (stderr, _("%s: illegal runlevel: %s\n"), program_name, args[0]); nih_main_suggest_help (); exit (1); } /* Check we're root */ setuid (geteuid ()); if (getuid ()) { nih_fatal (_("Need to be root")); exit (1); } /* Send the appropriate message */ runlevel = args[0][0]; switch (runlevel) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': ret = sysv_change_runlevel (runlevel, extra_env, NULL, NULL); break; case 'S': case 's': ret = sysv_change_runlevel ('S', extra_env, NULL, NULL); break; case 'Q': case 'q': ret = kill (1, SIGHUP); if (ret < 0) nih_error_raise_system (); break; case 'U': case 'u': ret = kill (1, SIGTERM); if (ret < 0) nih_error_raise_system (); break; default: nih_assert_not_reached (); } if (ret < 0) { NihError *err; err = nih_error_get (); nih_error ("%s", err->message); nih_free (err); exit (1); } return 0; }
int setup_proxy(void) { bool exists_upper = false, exists_lower = false; NihError *err; /* When running in container, /sys/fs/cgroup will have been already mounted. But it may be ro */ if (is_ro_mount(CGDIR)) turn_mount_rw(CGDIR); /* * If /sys/fs/cgroup/cgmanager.lower exists, * if /sys/fs/cgroup/cgmanager exists, then exit (proxy already running) * start up, connect to .lower * else * if /sys/fs/cgroup/cgmanager exists, move it to /sys/fs/cgroup/cgmanager.lower * start up and connect to .lower */ server_conn = nih_dbus_connect(CGMANAGER_DBUS_PATH, NULL); if (server_conn) { exists_upper = true; dbus_connection_unref (server_conn); } else { err = nih_error_get(); nih_free(err); } server_conn = nih_dbus_connect(CGPROXY_DBUS_PATH, cgm_dbus_disconnected); if (server_conn) { exists_lower = true; } else { err = nih_error_get(); nih_free(err); } if (exists_upper && exists_lower) { dbus_connection_unref (server_conn); nih_fatal("proxy already running"); return -1; // proxy already running } if (exists_lower) // we've got the sock we need, all set. return 0; if (exists_upper) { //move /sys/fs/cgroup/cgmanager to /sys/fs/cgroup/cgmanager.lower if (mkdir(CGPROXY_DIR, 0755) < 0 && errno != EEXIST) { nih_fatal("failed to create lower sock"); return -1; } if (mount(CGMANAGER_DIR, CGPROXY_DIR, "none", MS_MOVE, 0) < 0) { /* it wasn't a mount, meaning we are at the host * level on an old kernel. So rename it */ if (unlink(CGPROXY_SOCK) && errno != ENOENT) nih_warn("failed to remove %s: %s", CGPROXY_SOCK, strerror(errno)); if (rmdir(CGPROXY_DIR) && errno != ENOENT) nih_warn("failed to remove %s: %s", CGPROXY_DIR, strerror(errno)); if (rename(CGMANAGER_DIR, CGPROXY_DIR) < 0) { nih_fatal("unable to rename the socket"); return -1; } if (mkdir(CGMANAGER_DIR, 0755) < 0) { nih_fatal("unable to create socket dir"); return -1; } } } server_conn = nih_dbus_connect(CGPROXY_DBUS_PATH, cgm_dbus_disconnected); if (!server_conn) { err = nih_error_get(); nih_fatal("Failed to open connection to %s: %s", CGPROXY_DBUS_PATH, err->message); nih_free(err); return -1; } return 0; }
int main (int argc, char *argv[]) { char ** args; int ret; DBusServer * server; struct stat sb; nih_main_init (argv[0]); nih_option_set_synopsis (_("Control group proxy")); nih_option_set_help (_("The cgroup manager proxy")); args = nih_option_parser (NULL, argc, argv, options, FALSE); if (! args) exit (1); if (geteuid() != 0) { nih_error("%s: Cgmanager proxy must be run as root", __func__); exit(1); } /* * If we are called with checkmaster, then only check whether * cgmanager is running. This is used by the init script to * determine whether to run cgmanager or cgproxy */ if (checkmaster) { if (master_running()) exit(0); exit(1); } if (setup_proxy() < 0) { nih_fatal ("Failed to set up as proxy"); exit(1); } /* Setup the DBus server */ server = nih_dbus_server ( CGMANAGER_DBUS_PATH, client_connect, client_disconnect); nih_assert (server != NULL); if (stat("/proc/self/ns/pid", &sb) == 0) { mypidns = read_pid_ns_link(getpid()); setns_pid_supported = true; } if (stat("/proc/self/ns/user", &sb) == 0) { myuserns = read_user_ns_link(getpid()); setns_user_supported = true; } /* Become daemon */ if (daemonise) { if (nih_main_daemonise () < 0) { NihError *err; err = nih_error_get (); nih_fatal ("%s: %s", _("Unable to become daemon"), err->message); nih_free (err); exit (1); } } /* * We have to send a message to force fd passing over the dbus * link to be negotiated. Else when we try to attach an fd we'll * fail. */ if (!send_dummy_msg(server_conn)) { nih_fatal("Failed to send opening ping to cgmanager"); exit(1); } if (sigstop) raise(SIGSTOP); ret = nih_main_loop (); return ret; }
int main (int argc, char *argv[]) { char ** args; nih_local char *message = NULL; size_t messagelen; nih_local char *msg = NULL; int arg; pid_t pid = 0; nih_main_init (argv[0]); nih_option_set_usage (_("TIME [MESSAGE]")); nih_option_set_synopsis (_("Bring the system down.")); nih_option_set_help ( _("TIME may have different formats, the most common is simply " "the word 'now' which will bring the system down " "immediately. Other valid formats are +m, where m is the " "number of minutes to wait until shutting down and hh:mm " "which specifies the time on the 24hr clock.\n" "\n" "Logged in users are warned by a message sent to their " "terminal, you may include an optional MESSAGE included " "with this. Messages can be sent without actually " "bringing the system down by using the -k option.\n" "\n" "If TIME is given, the command will remain in the " "foreground until the shutdown occurs. It can be cancelled " "by Control-C, or by another user using the -c option.\n" "\n" "The system is brought down into maintenance (single-user) " "mode by default, you can change this with either the -r or " "-h option which specify a reboot or system halt " "respectively. The -h option can be further modified with " "-H or -P to specify whether to halt the system, or to " "power it off afterwards. The default is left up to the " "shutdown scripts.")); args = nih_option_parser (NULL, argc, argv, options, FALSE); if (! args) exit (1); /* If the runlevel wasn't given explicitly, set it to 1 so we go * down into single-user mode. */ if (! runlevel) { runlevel = '1'; init_halt = NULL; } /* When may be specified with -g, or must be first argument */ if (! (cancel || when || args[0])) { fprintf (stderr, _("%s: time expected\n"), program_name); nih_main_suggest_help (); exit (1); } else if (! (cancel || when)) { when = NIH_MUST (nih_strdup (NULL, args[0])); arg = 1; } else { arg = 0; } /* Parse the time argument */ if (when) { if (! strcmp (when, "now")) { /* "now" means, err, now */ delay = 0; } else if (strchr (when, ':')) { /* Clock time */ long hours, mins; char *endptr; struct tm *tm; time_t now; hours = strtoul (when, &endptr, 10); if ((*endptr != ':') || (hours < 0) || (hours > 23)) { fprintf (stderr, _("%s: illegal hour value\n"), program_name); nih_main_suggest_help (); exit (1); } mins = strtoul (endptr + 1, &endptr, 10); if (*endptr || (mins < 0) || (mins > 59)) { fprintf (stderr, _("%s: illegal minute value\n"), program_name); nih_main_suggest_help (); exit (1); } /* Subtract the current time to get the delay. * Add a whole day if we go negative */ now = time (NULL); tm = localtime (&now); delay = (((hours * 60) + mins) - ((tm->tm_hour * 60) + tm->tm_min)); if (delay < 0) delay += 1440; } else { /* Delay in minutes */ char *endptr; delay = strtoul (when, &endptr, 10); if (*endptr || (delay < 0)) { fprintf (stderr, _("%s: illegal time value\n"), program_name); nih_main_suggest_help (); exit (1); } } nih_free (when); } /* The rest of the arguments are a message. * Really this should be just the next argument, but that's not * how this has been traditionally done *sigh* */ message = NIH_MUST (nih_strdup (NULL, "")); messagelen = 0; for (; args[arg]; arg++) { message = NIH_MUST (nih_realloc ( message, NULL, messagelen + strlen(args[arg]) + 4)); strcat (message, args[arg]); strcat (message, " "); messagelen += strlen (args[arg]) + 1; } /* Terminate with \r\n */ if (messagelen) strcat (message, "\r\n"); /* Check we're root, or setuid root */ setuid (geteuid ()); if (getuid ()) { nih_fatal (_("Need to be root")); exit (1); } /* Look for an existing pid file and deal with the existing * process if there is one. */ pid = nih_main_read_pidfile (); if (pid > 0) { if (cancel) { if (kill (pid, SIGINT) < 0) { nih_error (_("Shutdown is not running")); exit (1); } if (messagelen) wall (message); exit (0); } else if (kill (pid, 0) == 0) { nih_error (_("Another shutdown is already running")); exit (1); } } else if (cancel) { nih_error (_("Cannot find pid of running shutdown")); exit (1); } /* Send an initial message */ msg = NIH_MUST (warning_message (message)); wall (msg); if (warn_only) exit (0); /* Give us a sane environment */ if (chdir ("/") < 0) nih_warn ("%s: %s", _("Unable to change directory"), strerror (errno)); umask (022); /* Shutdown now? */ if (! delay) shutdown_now (); /* Save our pid so we can be interrupted later */ if (nih_main_write_pidfile (getpid ()) < 0) { NihError *err; err = nih_error_get (); nih_warn ("%s: %s: %s", nih_main_get_pidfile(), _("Unable to write pid file"), err->message); nih_free (err); } /* Ignore a whole bunch of signals */ nih_signal_set_ignore (SIGCHLD); nih_signal_set_ignore (SIGHUP); nih_signal_set_ignore (SIGTSTP); nih_signal_set_ignore (SIGTTIN); nih_signal_set_ignore (SIGTTOU); /* Catch the usual quit signals */ nih_signal_set_handler (SIGINT, nih_signal_handler); NIH_MUST (nih_signal_add_handler (NULL, SIGINT, cancel_callback, NULL)); nih_signal_set_handler (SIGQUIT, nih_signal_handler); NIH_MUST (nih_signal_add_handler (NULL, SIGQUIT, cancel_callback, NULL)); nih_signal_set_handler (SIGTERM, nih_signal_handler); NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, cancel_callback, NULL)); /* Call a timer every minute until we shutdown */ NIH_MUST (nih_timer_add_periodic (NULL, 60, (NihTimerCb)timer_callback, message)); /* Hang around */ nih_main_loop (); return 0; }
/** * Mount the cgroup filesystems and record the information. * This should take configuration data from /etc. For now, * Just mount all controllers, separately just as cgroup-lite * does, and set the use_hierarchy and clone_children options. * * Things which should go into configuration file: * . which controllers to mount * . which controllers to co-mount * . any mount options (per-controller) * . values for sane_behavior, use_hierarchy, and clone_children */ int setup_cgroup_mounts(void) { FILE *cgf; int ret; char line[400]; if (unshare(CLONE_NEWNS) < 0) { nih_fatal("Failed to unshare a private mount ns: %s", strerror(errno)); return -1; } if (!setup_base_path()) { nih_fatal("Error setting up base cgroup path"); return -1; } if ((cgf = fopen("/proc/cgroups", "r")) == NULL) { nih_fatal ("Error opening /proc/cgroups: %s", strerror(errno)); return -1; } while (fgets(line, 400, cgf)) { char *p; struct controller_mounts *tmp; char dest[MAXPATHLEN]; unsigned long h; if (line[0] == '#') continue; p = index(line, '\t'); if (!p) continue; *p = '\0'; h = strtoul(p+1, NULL, 10); if (h) { nih_info("%s was already mounted!", line); #if STRICT ret = -1; goto out; #endif } ret = snprintf(dest, MAXPATHLEN, "%s/%s", base_path, line); if (ret < 0 || ret >= MAXPATHLEN) { nih_fatal("Error calculating pathname for %s and %s", base_path, line); goto out; } if (mkdir(dest, 0755) < 0 && errno != EEXIST) { nih_fatal("Failed to create %s: %s", dest, strerror(errno)); ret = -1; goto out; } if ((ret = mount(line, dest, "cgroup", 0, line)) < 0) { nih_fatal("Failed mounting %s: %s", line, strerror(errno)); goto out; } ret = -1; tmp = realloc(all_mounts, (num_controllers+1) * sizeof(*all_mounts)); if (!tmp) { nih_fatal("Out of memory mounting controllers"); goto out; } all_mounts = tmp; all_mounts[num_controllers].controller = strdup(line); if (!all_mounts[num_controllers].controller) { nih_fatal("Out of memory mounting controllers"); goto out; } all_mounts[num_controllers].options = NULL; all_mounts[num_controllers].path = strdup(dest); if (!all_mounts[num_controllers].path) { nih_fatal("Out of memory mounting controllers"); goto out; } nih_info("Mounted %s onto %s", all_mounts[num_controllers].controller, all_mounts[num_controllers].path); if (strcmp(all_mounts[num_controllers].controller, "cpuset") == 0) { set_clone_children(dest); // TODO make this optional? nih_info("set clone_children"); } else if (strcmp(all_mounts[num_controllers].controller, "memory") == 0) { set_use_hierarchy(dest); // TODO make this optional? nih_info("set memory.use_hierarchy"); } num_controllers++; } nih_info("mounted %d controllers", num_controllers); ret = 0; out: fclose(cgf); return ret; }
int main (int argc, char *argv[]) { char ** args; DBusConnection * connection; struct udev * udev; struct udev_monitor *udev_monitor; int ret; nih_main_init (argv[0]); nih_option_set_synopsis (_("Bridge udev events into upstart")); nih_option_set_help ( _("By default, upstart-udev-bridge does not detach from the " "console and remains in the foreground. Use the --daemon " "option to have it detach.")); args = nih_option_parser (NULL, argc, argv, options, FALSE); if (! args) exit (1); /* Initialise the connection to Upstart */ connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_UPSTART, upstart_disconnected)); if (! connection) { NihError *err; err = nih_error_get (); nih_fatal ("%s: %s", _("Could not connect to Upstart"), err->message); nih_free (err); exit (1); } upstart = NIH_SHOULD (nih_dbus_proxy_new (NULL, connection, NULL, DBUS_PATH_UPSTART, NULL, NULL)); if (! upstart) { NihError *err; err = nih_error_get (); nih_fatal ("%s: %s", _("Could not create Upstart proxy"), err->message); nih_free (err); exit (1); } /* Initialise the connection to udev */ nih_assert (udev = udev_new ()); nih_assert (udev_monitor = udev_monitor_new_from_netlink (udev, "udev")); nih_assert (udev_monitor_enable_receiving (udev_monitor) == 0); udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); NIH_MUST (nih_io_add_watch (NULL, udev_monitor_get_fd (udev_monitor), NIH_IO_READ, (NihIoWatcher)udev_monitor_watcher, udev_monitor)); /* Become daemon */ if (daemonise) { if (nih_main_daemonise () < 0) { NihError *err; err = nih_error_get (); nih_fatal ("%s: %s", _("Unable to become daemon"), err->message); nih_free (err); exit (1); } /* Send all logging output to syslog */ openlog (program_name, LOG_PID, LOG_DAEMON); nih_log_set_logger (nih_logger_syslog); } /* Handle TERM and INT signals gracefully */ nih_signal_set_handler (SIGTERM, nih_signal_handler); NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, nih_main_term_signal, NULL)); if (! daemonise) { nih_signal_set_handler (SIGINT, nih_signal_handler); NIH_MUST (nih_signal_add_handler (NULL, SIGINT, nih_main_term_signal, NULL)); } ret = nih_main_loop (); return ret; }
static void upstart_disconnected (DBusConnection *connection) { nih_fatal (_("Disconnected from Upstart")); nih_main_loop_exit (1); }