/** * check_for_overlayfs: * * Determine if the mount point used by the tests for creating temporary * files is using overlayfs. * * Returns: TRUE if temporary work area is on overlayfs, else FALSE. **/ int check_for_overlayfs (void) { struct statfs statbuf; char path[PATH_MAX]; int found = FALSE; /* Create a file in the temporary work area */ TEST_FILENAME (path); fclose (fopen (path, "w")); /* Check it exits */ assert0 (statfs (path, &statbuf)); if (statbuf.f_type == OVERLAYFS_SUPER_MAGIC) { nih_warn ("Mountpoint for '%s' (needed by the Upstart tests) is an overlayfs " "filesystem, which does not support inotify.", path); found = TRUE; } assert0 (unlink (path)); return found; }
int check_cgroup_sandbox(void) { char *cg_prev = NULL, *cg_post = NULL; int ret = -1; cg_prev = get_my_cgroup(); if (!cg_prev) return -1; if (setup_cgroup_sandbox() < 0) { nih_free(cg_prev); return -1; } cg_post = get_my_cgroup(); if (!cg_post) { nih_free(cg_prev); return -1; } /* we should have moved cgroups, so the two should be different */ if (strcmp(cg_prev, cg_post) != 0) { nih_warn("setup_cgroup_sandbox moved me from %s to %s", cg_prev, cg_post); ret = 0; } nih_free(cg_prev); nih_free(cg_post); return ret; }
/** * test_checks: * * Perform any checks necessary before real tests are run. **/ void test_checks (void) { int ret; TEST_GROUP ("test environment"); /* * Warn (*) if overlayfs detected. * * (*) - Don't fail in the hope that one day someone might fix * overlayfs. */ TEST_FEATURE ("checking for overlayfs"); if (check_for_overlayfs ()) { nih_warn ("Found overlayfs mounts"); nih_warn ("This environment will probably cause tests to fail mysteriously!!"); nih_warn ("See bug LP:#882147 for further details."); } #ifdef ENABLE_CGROUPS if (file_exists ("/sys/fs/cgroup/cgmanager/sock")) { TEST_FEATURE ("checking for cgmanager"); ret = connect_to_cgmanager (); switch (ret) { case -2: nih_warn ("Found no cgroup manager"); goto out_skip; case -1: nih_warn ("Error connecting to cgmanager"); goto out_skip; case 0: print_my_cgroup (); break; default: nih_warn ("Unknown error from connect_to_cgmanager: %d", ret); goto out_skip; } TEST_FEATURE ("cgroup sandbox"); if (check_cgroup_sandbox() != 0) nih_warn ("Could not create cgroup sandbox"); } else { nih_warn ("Skipping CGManager tests, CGManager socket not found"); } out_skip: disconnect_cgmanager(); if (ret) nih_warn ("Skipping CGManager tests, CGManager not properly configured"); #endif /* ENABLE_CGROUPS */ }
static void emit_event_error (void * data, NihDBusMessage *message) { NihError *err; err = nih_error_get (); nih_warn ("%s", err->message); nih_free (err); }
void print_my_cgroup(void) { char *str; str = get_pid_cgroup("freezer", getpid()); if (str) { nih_warn("I am in freezer cgroup: %s", str); TEST_EQ_STR(str, "/"); nih_free(str); } else { TEST_FAILED("Failed to get my freezer cgroup"); } }
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 get_tasks_recursive_main (void *parent, const char *controller, const char *cgroup, struct ucred p, struct ucred r, int32_t **pids) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; uint32_t nrpids; struct ucred tcred; int i; if (memcmp(&p, &r, sizeof(struct ucred)) != 0) { nih_error("%s: proxy != requestor", __func__); return -1; } if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } if (!(message = start_dbus_request("GetTasksRecursiveScm", sv))) { nih_error("%s: error starting dbus request", __func__); return -1; } dbus_message_iter_init_append(message, &iter); if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &controller)) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &cgroup)) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_UNIX_FD, &sv[1])) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (!complete_dbus_request(message, sv, &r, NULL)) { nih_error("%s: error completing dbus request", __func__); goto out; } if (proxyrecv(sv[0], &nrpids, sizeof(uint32_t)) != sizeof(uint32_t)) goto out; if (nrpids == -1) { nih_error("%s: bad cgroup: %s:%s", __func__, controller, cgroup); ret = -1; goto out; } if (nrpids == 0) { ret = 0; goto out; } *pids = NIH_MUST( nih_alloc(parent, nrpids * sizeof(uint32_t)) ); for (i=0; i<nrpids; i++) { get_scm_creds_sync(sv[0], &tcred); if (tcred.pid == -1) { nih_warn("%s: Failed getting pid from server", __func__); goto out; } (*pids)[i] = tcred.pid; } ret = nrpids; out: close(sv[0]); close(sv[1]); return ret; }
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; }
/** * wall: * @message: message to send. * * Send a message to all logged in users; based largely on the code from * bsdutils. This is done in a child process to stop anything blocking. **/ static void wall (const char *message) { struct sigaction act; struct utmpx * ent; pid_t pid; time_t now; struct tm * tm; char * user; char * tty; char hostname[MAXHOSTNAMELEN]; char * banner1; char * banner2; pid = fork (); if (pid < 0) { nih_warn (_("Unable to fork child-process to warn users: %s"), strerror (errno)); return; } else if (pid > 0) { return; } /* Break syscalls with SIGALRM */ act.sa_handler = alarm_handler; act.sa_flags = 0; sigemptyset (&act.sa_mask); sigaction (SIGALRM, &act, NULL); /* Get username for banner */ user = getlogin (); if (! user) { struct passwd *pw; pw = getpwuid (getuid ()); if (pw) user = pw->pw_name; } if (! user) { if (getuid ()) { user = NIH_MUST (nih_sprintf (NULL, "uid %d", getuid ())); } else { user = "******"; } } /* Get hostname for banner */ gethostname (hostname, sizeof (hostname)); /* Get terminal for banner */ tty = ttyname (0); if (! tty) tty = "unknown"; /* Get time */ now = time (NULL); tm = localtime (&now); /* Construct banner */ banner1 = nih_sprintf (NULL, _("Broadcast message from %s@%s"), user, hostname); banner2 = nih_sprintf (NULL, _("(%s) at %d:%02d ..."), tty, tm->tm_hour, tm->tm_min); /* Iterate entries in the utmp file */ setutxent (); while ((ent = getutxent ()) != NULL) { char dev[PATH_MAX + 1]; int fd; /* Ignore entries without a name, or not a user process */ if ((ent->ut_type != USER_PROCESS) || (! strlen (ent->ut_user))) continue; /* Construct the device path */ if (strncmp (ent->ut_line, DEV "/", 5)) { snprintf (dev, sizeof (dev), "%s/%s", DEV, ent->ut_line); } else { snprintf (dev, sizeof (dev), "%s", ent->ut_line); } alarm (2); fd = open (dev, O_WRONLY | O_NDELAY | O_NOCTTY); if ((fd >= 0) && isatty (fd)) { FILE *term; term = fdopen (fd, "w"); if (term) { fprintf (term, "\007\r\n%s\r\n\t%s\r\n\r\n", banner1, banner2); fputs (message, term); fflush (term); fclose (term); } } alarm (0); } endutxent (); nih_free (banner1); nih_free (banner2); exit (0); }
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; }
static void udev_monitor_watcher (struct udev_monitor *udev_monitor, NihIoWatch * watch, NihIoEvents events) { struct udev_device * udev_device; nih_local char * subsystem = NULL; nih_local char * action = NULL; nih_local char * kernel = NULL; nih_local char * devpath = NULL; nih_local char * devname = NULL; nih_local char * name = NULL; nih_local char ** env = NULL; const char * value = NULL; size_t env_len = 0; DBusPendingCall * pending_call; char *(*copy_string)(const void *, const char *) = NULL; udev_device = udev_monitor_receive_device (udev_monitor); if (! udev_device) return; copy_string = no_strip_udev_data ? nih_strdup : make_safe_string; value = udev_device_get_subsystem (udev_device); subsystem = value ? copy_string (NULL, value) : NULL; value = udev_device_get_action (udev_device); action = value ? copy_string (NULL, value) : NULL; value = udev_device_get_sysname (udev_device); kernel = value ? copy_string (NULL, value) : NULL; value = udev_device_get_devpath (udev_device); devpath = value ? copy_string (NULL, value) : NULL; value = udev_device_get_devnode (udev_device); devname = value ? copy_string (NULL, value) : NULL; /* Protect against the "impossible" */ if (! action) goto out; if (! strcmp (action, "add")) { name = NIH_MUST (nih_sprintf (NULL, "%s-device-added", subsystem)); } else if (! strcmp (action, "change")) { name = NIH_MUST (nih_sprintf (NULL, "%s-device-changed", subsystem)); } else if (! strcmp (action, "remove")) { name = NIH_MUST (nih_sprintf (NULL, "%s-device-removed", subsystem)); } else { name = NIH_MUST (nih_sprintf (NULL, "%s-device-%s", subsystem, action)); } env = NIH_MUST (nih_str_array_new (NULL)); if (kernel) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "KERNEL=%s", kernel)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (devpath) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "DEVPATH=%s", devpath)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (devname) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "DEVNAME=%s", devname)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (subsystem) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "SUBSYSTEM=%s", subsystem)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (action) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "ACTION=%s", action)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } for (struct udev_list_entry *list_entry = udev_device_get_properties_list_entry (udev_device); list_entry != NULL; list_entry = udev_list_entry_get_next (list_entry)) { nih_local char *udev_name = NULL; nih_local char *udev_value = NULL; nih_local char *var = NULL; udev_name = copy_string (NULL, udev_list_entry_get_name (list_entry)); if (! strcmp (udev_name, "DEVPATH")) continue; if (! strcmp (udev_name, "DEVNAME")) continue; if (! strcmp (udev_name, "SUBSYSTEM")) continue; if (! strcmp (udev_name, "ACTION")) continue; udev_value = copy_string (NULL, udev_list_entry_get_value (list_entry)); var = NIH_MUST (nih_sprintf (NULL, "%s=%s", udev_name, udev_value)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } nih_debug ("%s %s", name, devname ? devname : ""); pending_call = upstart_emit_event (upstart, name, env, FALSE, NULL, emit_event_error, NULL, NIH_DBUS_TIMEOUT_NEVER); if (! pending_call) { NihError *err; int saved = errno; err = nih_error_get (); nih_warn ("%s", err->message); if (saved != ENOMEM && subsystem) nih_warn ("Likely that udev '%s' event contains binary garbage", subsystem); nih_free (err); } dbus_pending_call_unref (pending_call); out: udev_device_unref (udev_device); }