char *cgm_get_pid_cgroup(pid_t pid, const char *controller) { char *output = NULL; if (!cgm_dbus_connect()) { return NULL; } if ( cgmanager_get_pid_cgroup_sync(NULL, cgroup_manager, controller, pid, &output) != 0 ) { NihError *nerr; nerr = nih_error_get(); fprintf(stderr, "call to get_pid_cgroup (%s) failed: %s\n", controller, nerr->message); nih_free(nerr); cgm_dbus_disconnect(); return NULL; } cgm_dbus_disconnect(); return output; }
/* * Use the cgmanager to move a task into a cgroup for a particular * hierarchy. * All the subsystems in this hierarchy are co-mounted, so we only * need to transition the task into one of the cgroups * * Internal helper, must be called with cgmanager dbus socket open */ static bool lxc_cgmanager_enter(pid_t pid, const char *controller, const char *cgroup_path, bool abs) { int ret; if (abs) ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager, controller, cgroup_path, pid); else ret = cgmanager_move_pid_sync(NULL, cgroup_manager, controller, cgroup_path, pid); if (ret != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_move_pid_%ssync failed: %s", abs ? "abs_" : "", nerr->message); nih_free(nerr); return false; } return true; }
/** * 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); }
static char *try_get_abs_cgroup(const char *name, const char *lxcpath, const char *controller) { char *cgroup = NULL; if (abs_cgroup_supported()) { /* get the container init pid and ask for its abs cgroup */ pid_t pid = lxc_cmd_get_init_pid(name, lxcpath); if (pid < 0) return NULL; if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager, controller, pid, &cgroup) != 0) { cgroup = NULL; NihError *nerr; nerr = nih_error_get(); nih_free(nerr); } return cgroup; } /* use the command interface to look for the cgroup */ return lxc_cmd_get_cgroup_path(name, lxcpath, controller); }
/* * Escape to the root cgroup if we are root, so that the container will * be in "/lxc/c1" rather than "/user/..../c1" * called internally with connection already open */ static bool cgm_escape(void) { bool ret = true, cgm_needs_disconnect = false; pid_t me = getpid(); char **slist = subsystems; int i; if (!cgroup_manager) { if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } cgm_needs_disconnect = true; } if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) { if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager, slist[i], "/", me) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s", slist[i], nerr->message); nih_free(nerr); ret = false; break; } } if (cgm_needs_disconnect) cgm_dbus_disconnect(); return ret; }
bool cgm_remove(const char *controller, const char *cg) { /* * tempting to make remove be recursive, but this is a filesystem, * so best to opt for least surprise */ int32_t r = 0, e; if (!cgm_dbus_connect()) { return false; } if ( cgmanager_remove_sync(NULL, cgroup_manager, controller, cg, r, &e) != 0) { NihError *nerr; nerr = nih_error_get(); fprintf(stderr, "call to remove (%s:%s) failed: %s\n", controller, cg, nerr->message); nih_free(nerr); cgm_dbus_disconnect(); return false; } cgm_dbus_disconnect(); return true; }
/* unfreeze is called by the command api after killing a container. */ static bool cgm_unfreeze(void *hdata) { struct cgm_data *d = hdata; bool ret = true; if (!d || !d->cgroup_path) return false; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path, "freezer.state", "THAWED") != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); ERROR("Error unfreezing %s", d->cgroup_path); ret = false; } cgm_dbus_disconnect(); return ret; }
DBusHandlerResult my_com_netsplit_Nih_Test_Method_method (NihDBusObject * object, NihDBusMessage *message) { DBusMessageIter iter; DBusMessage * reply; MyMethodStructure *structure; DBusMessageIter structure_iter; const char * structure_item0; uint32_t structure_item1; nih_assert (object != NULL); nih_assert (message != NULL); /* Iterate the arguments to the message and demarshal into arguments * for our own function call. */ dbus_message_iter_init (message->message, &iter); if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) { reply = dbus_message_new_error (message->message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to Method method"); if (! reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (! dbus_connection_send (message->connection, reply, NULL)) { dbus_message_unref (reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } /* Call the handler function */ nih_error_push_context (); if (my_method (object->data, message, &structure) < 0) { NihError *err; err = nih_error_get (); if (err->number == ENOMEM) { nih_free (err); nih_error_pop_context (); return DBUS_HANDLER_RESULT_NEED_MEMORY; } else if (err->number == NIH_DBUS_ERROR) { NihDBusError *dbus_err = (NihDBusError *)err; reply = NIH_MUST (dbus_message_new_error (message->message, dbus_err->name, err->message)); nih_free (err); nih_error_pop_context (); NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } else { reply = NIH_MUST (dbus_message_new_error (message->message, DBUS_ERROR_FAILED, err->message)); nih_free (err); nih_error_pop_context (); NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } } nih_error_pop_context (); /* If the sender doesn't care about a reply, don't bother wasting * effort constructing and sending one. */ if (dbus_message_get_no_reply (message->message)) return DBUS_HANDLER_RESULT_HANDLED; do { __label__ enomem; /* Construct the reply message. */ reply = dbus_message_new_method_return (message->message); if (! reply) goto enomem; dbus_message_iter_init_append (reply, &iter); /* Marshal a structure onto the message */ if (! dbus_message_iter_open_container (&iter, DBUS_TYPE_STRUCT, NULL, &structure_iter)) { dbus_message_unref (reply); reply = NULL; goto enomem; } structure_item0 = structure->item0; /* Marshal a char * onto the message */ if (! dbus_message_iter_append_basic (&structure_iter, DBUS_TYPE_STRING, &structure_item0)) { dbus_message_iter_abandon_container (&iter, &structure_iter); dbus_message_unref (reply); reply = NULL; goto enomem; } structure_item1 = structure->item1; /* Marshal a uint32_t onto the message */ if (! dbus_message_iter_append_basic (&structure_iter, DBUS_TYPE_UINT32, &structure_item1)) { dbus_message_iter_abandon_container (&iter, &structure_iter); dbus_message_unref (reply); reply = NULL; goto enomem; } if (! dbus_message_iter_close_container (&iter, &structure_iter)) { dbus_message_unref (reply); reply = NULL; goto enomem; } enomem: __attribute__ ((unused)); } while (! reply); /* Send the reply, appending it to the outgoing queue. */ NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; }
static int do_chown_cgroup(const char *controller, const char *cgroup_path, uid_t newuid) { int sv[2] = {-1, -1}, optval = 1, ret = -1; char buf[1]; struct pollfd fds; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) { SYSERROR("Error creating socketpair"); goto out; } if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { SYSERROR("setsockopt failed"); goto out; } if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { SYSERROR("setsockopt failed"); goto out; } if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller, cgroup_path, sv[1]) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message); nih_free(nerr); goto out; } /* now send credentials */ fds.fd = sv[0]; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, -1) <= 0) { ERROR("Error getting go-ahead from server: %s", strerror(errno)); goto out; } if (read(sv[0], &buf, 1) != 1) { ERROR("Error getting reply from server over socketpair"); goto out; } if (send_creds(sv[0], getpid(), getuid(), getgid())) { SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__); goto out; } fds.fd = sv[0]; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, -1) <= 0) { ERROR("Error getting go-ahead from server: %s", strerror(errno)); goto out; } if (read(sv[0], &buf, 1) != 1) { ERROR("Error getting reply from server over socketpair"); goto out; } if (send_creds(sv[0], getpid(), newuid, 0)) { SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__); goto out; } fds.fd = sv[0]; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, -1) <= 0) { ERROR("Error getting go-ahead from server: %s", strerror(errno)); goto out; } ret = read(sv[0], buf, 1); out: close(sv[0]); close(sv[1]); if (ret == 1 && *buf == '1') return 0; return -1; }
static DBusHandlerResult my_com_netsplit_Nih_Test_Peek_method (NihDBusObject * object, NihDBusMessage *message) { DBusMessageIter iter; DBusMessage * reply; uint32_t address; nih_assert (object != NULL); nih_assert (message != NULL); /* Iterate the arguments to the message and demarshal into arguments * for our own function call. */ dbus_message_iter_init (message->message, &iter); /* Demarshal a uint32_t from the message */ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_UINT32) { reply = dbus_message_new_error (message->message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to Peek method"); if (! reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (! dbus_connection_send (message->connection, reply, NULL)) { dbus_message_unref (reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } dbus_message_iter_get_basic (&iter, &address); dbus_message_iter_next (&iter); if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) { reply = dbus_message_new_error (message->message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to Peek method"); if (! reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (! dbus_connection_send (message->connection, reply, NULL)) { dbus_message_unref (reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } /* Call the handler function */ nih_error_push_context (); if (my_test_peek (object->data, message, address) < 0) { NihError *err; err = nih_error_get (); if (err->number == ENOMEM) { nih_free (err); nih_error_pop_context (); return DBUS_HANDLER_RESULT_NEED_MEMORY; } else if (err->number == NIH_DBUS_ERROR) { NihDBusError *dbus_err = (NihDBusError *)err; reply = NIH_MUST (dbus_message_new_error (message->message, dbus_err->name, err->message)); nih_free (err); nih_error_pop_context (); NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } else { reply = NIH_MUST (dbus_message_new_error (message->message, DBUS_ERROR_FAILED, err->message)); nih_free (err); nih_error_pop_context (); NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } } nih_error_pop_context (); return DBUS_HANDLER_RESULT_HANDLED; }
void test_output (void) { FILE * source; FILE * header; Node * node = NULL; Interface * interface = NULL; Method * method = NULL; Signal * signal = NULL; Argument * argument = NULL; Property * property = NULL; int ret; NihError * err; TEST_FUNCTION ("output"); source = tmpfile (); header = tmpfile (); /* Check that we can generate a valid source file and accompanying * header file for a node in proxy mode. */ TEST_FEATURE ("with proxy"); TEST_ALLOC_FAIL { TEST_ALLOC_SAFE { node = node_new (NULL, NULL); interface = interface_new (node, "com.netsplit.Nih.Test"); interface->symbol = "test"; nih_list_add (&node->interfaces, &interface->entry); method = method_new (interface, "Poke"); method->symbol = "poke"; nih_list_add (&interface->methods, &method->entry); argument = argument_new (method, "address", "u", NIH_DBUS_ARG_IN); argument->symbol = "address"; nih_list_add (&method->arguments, &argument->entry); argument = argument_new (method, "value", "s", NIH_DBUS_ARG_IN); argument->symbol = "value"; nih_list_add (&method->arguments, &argument->entry); method = method_new (interface, "Peek"); method->symbol = "peek"; nih_list_add (&interface->methods, &method->entry); argument = argument_new (method, "address", "u", NIH_DBUS_ARG_IN); argument->symbol = "address"; nih_list_add (&method->arguments, &argument->entry); argument = argument_new (method, "value", "s", NIH_DBUS_ARG_OUT); argument->symbol = "value"; nih_list_add (&method->arguments, &argument->entry); method = method_new (interface, "IsValidAddress"); method->symbol = "is_valid_address"; nih_list_add (&interface->methods, &method->entry); argument = argument_new (method, "address", "u", NIH_DBUS_ARG_IN); argument->symbol = "address"; nih_list_add (&method->arguments, &argument->entry); signal = signal_new (interface, "Bounce"); signal->symbol = "bounce"; nih_list_add (&interface->signals, &signal->entry); argument = argument_new (signal, "height", "u", NIH_DBUS_ARG_OUT); argument->symbol = "height"; nih_list_add (&signal->arguments, &argument->entry); argument = argument_new (signal, "velocity", "i", NIH_DBUS_ARG_OUT); argument->symbol = "velocity"; nih_list_add (&signal->arguments, &argument->entry); signal = signal_new (interface, "Exploded"); signal->symbol = "exploded"; nih_list_add (&interface->signals, &signal->entry); property = property_new (interface, "colour", "s", NIH_DBUS_READWRITE); property->symbol = "colour"; nih_list_add (&interface->properties, &property->entry); property = property_new (interface, "size", "u", NIH_DBUS_READ); property->symbol = "size"; nih_list_add (&interface->properties, &property->entry); property = property_new (interface, "touch", "b", NIH_DBUS_WRITE); property->symbol = "touch"; nih_list_add (&interface->properties, &property->entry); interface = interface_new (node, "com.netsplit.Nih.Foo"); interface->symbol = "foo"; nih_list_add (&node->interfaces, &interface->entry); method = method_new (interface, "Bing"); method->symbol = "bing"; nih_list_add (&interface->methods, &method->entry); signal = signal_new (interface, "NewResult"); signal->symbol = "new_result"; nih_list_add (&interface->signals, &signal->entry); property = property_new (interface, "preferences", "(us)", NIH_DBUS_READWRITE); property->symbol = "preferences"; nih_list_add (&interface->properties, &property->entry); } ret = output ("test.c", fileno (source), "test.h", fileno (header), "my", node, FALSE); rewind (source); rewind (header); if (test_alloc_failed) { TEST_LT (ret, 0); err = nih_error_get (); TEST_EQ (err->number, ENOMEM); nih_free (err); TEST_FILE_RESET (source); TEST_FILE_RESET (header); nih_free (node); continue; } TEST_EQ (ret, 0); TEST_EXPECTED_FILE (source, "test_output_proxy_standard.c"); TEST_EXPECTED_FILE (header, "test_output_proxy_standard.h"); nih_free (node); } /* Check that when there are no interfaces, a valid empty source * and header file are generated. */ TEST_FEATURE ("with proxy but no interfaces"); TEST_ALLOC_FAIL { TEST_ALLOC_SAFE { node = node_new (NULL, NULL); } ret = output ("test.c", fileno (source), "test.h", fileno (header), "my", node, FALSE); rewind (source); rewind (header); if (test_alloc_failed) { TEST_LT (ret, 0); err = nih_error_get (); TEST_EQ (err->number, ENOMEM); nih_free (err); TEST_FILE_RESET (source); TEST_FILE_RESET (header); nih_free (node); continue; } TEST_EQ (ret, 0); TEST_EXPECTED_FILE (source, "test_output_proxy_no_interfaces.c"); TEST_EXPECTED_FILE (header, "test_output_proxy_no_interfaces.h"); nih_free (node); } /* Check that we can generate a valid source file and accompanying * header file for a node in object mode. */ TEST_FEATURE ("with object"); TEST_ALLOC_FAIL { TEST_ALLOC_SAFE { node = node_new (NULL, NULL); interface = interface_new (node, "com.netsplit.Nih.Test"); interface->symbol = "test"; nih_list_add (&node->interfaces, &interface->entry); method = method_new (interface, "Poke"); method->symbol = "poke"; nih_list_add (&interface->methods, &method->entry); argument = argument_new (method, "address", "u", NIH_DBUS_ARG_IN); argument->symbol = "address"; nih_list_add (&method->arguments, &argument->entry); argument = argument_new (method, "value", "s", NIH_DBUS_ARG_IN); argument->symbol = "value"; nih_list_add (&method->arguments, &argument->entry); method = method_new (interface, "Peek"); method->symbol = "peek"; method->async = TRUE; nih_list_add (&interface->methods, &method->entry); argument = argument_new (method, "address", "u", NIH_DBUS_ARG_IN); argument->symbol = "address"; nih_list_add (&method->arguments, &argument->entry); argument = argument_new (method, "value", "s", NIH_DBUS_ARG_OUT); argument->symbol = "value"; nih_list_add (&method->arguments, &argument->entry); method = method_new (interface, "IsValidAddress"); method->symbol = "is_valid_address"; nih_list_add (&interface->methods, &method->entry); argument = argument_new (method, "address", "u", NIH_DBUS_ARG_IN); argument->symbol = "address"; nih_list_add (&method->arguments, &argument->entry); argument = argument_new (method, "is_valid", "b", NIH_DBUS_ARG_OUT); argument->symbol = "is_valid"; nih_list_add (&method->arguments, &argument->entry); signal = signal_new (interface, "Bounce"); signal->symbol = "bounce"; nih_list_add (&interface->signals, &signal->entry); argument = argument_new (signal, "height", "u", NIH_DBUS_ARG_OUT); argument->symbol = "height"; nih_list_add (&signal->arguments, &argument->entry); argument = argument_new (signal, "velocity", "i", NIH_DBUS_ARG_OUT); argument->symbol = "velocity"; nih_list_add (&signal->arguments, &argument->entry); signal = signal_new (interface, "Exploded"); signal->symbol = "exploded"; nih_list_add (&interface->signals, &signal->entry); property = property_new (interface, "colour", "s", NIH_DBUS_READWRITE); property->symbol = "colour"; nih_list_add (&interface->properties, &property->entry); property = property_new (interface, "size", "u", NIH_DBUS_READ); property->symbol = "size"; nih_list_add (&interface->properties, &property->entry); property = property_new (interface, "touch", "b", NIH_DBUS_WRITE); property->symbol = "touch"; nih_list_add (&interface->properties, &property->entry); interface = interface_new (node, "com.netsplit.Nih.Foo"); interface->symbol = "foo"; nih_list_add (&node->interfaces, &interface->entry); method = method_new (interface, "Bing"); method->symbol = "bing"; nih_list_add (&interface->methods, &method->entry); signal = signal_new (interface, "NewResult"); signal->symbol = "new_result"; nih_list_add (&interface->signals, &signal->entry); property = property_new (interface, "preferences", "(us)", NIH_DBUS_READWRITE); property->symbol = "preferences"; nih_list_add (&interface->properties, &property->entry); } ret = output ("test.c", fileno (source), "test.h", fileno (header), "my", node, TRUE); rewind (source); rewind (header); if (test_alloc_failed) { TEST_LT (ret, 0); err = nih_error_get (); TEST_EQ (err->number, ENOMEM); nih_free (err); TEST_FILE_RESET (source); TEST_FILE_RESET (header); nih_free (node); continue; } TEST_EQ (ret, 0); TEST_EXPECTED_FILE (source, "test_output_object_standard.c"); TEST_EXPECTED_FILE (header, "test_output_object_standard.h"); nih_free (node); } /* Check that when there are no interfaces, a valid empty source * and header file are generated. */ TEST_FEATURE ("with object but no interfaces"); TEST_ALLOC_FAIL { TEST_ALLOC_SAFE { node = node_new (NULL, NULL); } ret = output ("test.c", fileno (source), "test.h", fileno (header), "my", node, TRUE); rewind (source); rewind (header); if (test_alloc_failed) { TEST_LT (ret, 0); err = nih_error_get (); TEST_EQ (err->number, ENOMEM); nih_free (err); TEST_FILE_RESET (source); TEST_FILE_RESET (header); nih_free (node); continue; } TEST_EQ (ret, 0); TEST_EXPECTED_FILE (source, "test_output_object_no_interfaces.c"); TEST_EXPECTED_FILE (header, "test_output_object_no_interfaces.h"); nih_free (node); } fclose (source); fclose (header); }
static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp) { char *controller, *key, *cgroup = NULL; int retval = 0; // value we are sending to the parent over outp int ret; char *cglast; controller = alloca(strlen(filename)+1); strcpy(controller, filename); key = strchr(controller, '.'); if (!key) { ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } *key = '\0'; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } cgroup = try_get_abs_cgroup(name, lxcpath, controller); if (!cgroup) { cgm_dbus_disconnect(); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } cglast = strrchr(cgroup, '/'); if (!cglast) { cgm_dbus_disconnect(); free_abs_cgroup(cgroup); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } *cglast = '\0'; if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) { ERROR("Failed to enter container cgroup %s:%s", controller, cgroup); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); cgm_dbus_disconnect(); free_abs_cgroup(cgroup); exit(1); } if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup); ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); free_abs_cgroup(cgroup); cgm_dbus_disconnect(); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } free_abs_cgroup(cgroup); cgm_dbus_disconnect(); /* tell parent that we are done */ retval = 1; ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) { exit(1); } exit(0); }
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; }
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 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; }
static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue) { char *controller, *key, *cgroup = NULL, *cglast; int len = -1; int ret; nih_local char *result = NULL; controller = alloca(strlen(filename)+1); strcpy(controller, filename); key = strchr(controller, '.'); if (!key) { ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } *key = '\0'; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } cgroup = try_get_abs_cgroup(name, lxcpath, controller); if (!cgroup) { cgm_dbus_disconnect(); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } cglast = strrchr(cgroup, '/'); if (!cglast) { cgm_dbus_disconnect(); free_abs_cgroup(cgroup); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } *cglast = '\0'; if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) { ERROR("Failed to enter container cgroup %s:%s", controller, cgroup); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); cgm_dbus_disconnect(); free_abs_cgroup(cgroup); exit(1); } if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) { NihError *nerr; nerr = nih_error_get(); nih_free(nerr); free_abs_cgroup(cgroup); cgm_dbus_disconnect(); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } free_abs_cgroup(cgroup); cgm_dbus_disconnect(); len = strlen(result); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) { WARN("Failed to send length to parent"); exit(1); } if (!len || !sendvalue) { exit(0); } ret = write(outp, result, len); if (ret < 0) exit(1); exit(0); }
static DBusHandlerResult my_com_netsplit_Nih_Foo_Bing_method (NihDBusObject * object, NihDBusMessage *message) { DBusMessageIter iter; DBusMessage * reply; nih_assert (object != NULL); nih_assert (message != NULL); /* Iterate the arguments to the message and demarshal into arguments * for our own function call. */ dbus_message_iter_init (message->message, &iter); if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) { reply = dbus_message_new_error (message->message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to Bing method"); if (! reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (! dbus_connection_send (message->connection, reply, NULL)) { dbus_message_unref (reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } /* Call the handler function */ nih_error_push_context (); if (my_foo_bing (object->data, message) < 0) { NihError *err; err = nih_error_get (); if (err->number == ENOMEM) { nih_free (err); nih_error_pop_context (); return DBUS_HANDLER_RESULT_NEED_MEMORY; } else if (err->number == NIH_DBUS_ERROR) { NihDBusError *dbus_err = (NihDBusError *)err; reply = NIH_MUST (dbus_message_new_error (message->message, dbus_err->name, err->message)); nih_free (err); nih_error_pop_context (); NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } else { reply = NIH_MUST (dbus_message_new_error (message->message, DBUS_ERROR_FAILED, err->message)); nih_free (err); nih_error_pop_context (); NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; } } nih_error_pop_context (); /* If the sender doesn't care about a reply, don't bother wasting * effort constructing and sending one. */ if (dbus_message_get_no_reply (message->message)) return DBUS_HANDLER_RESULT_HANDLED; do { __label__ enomem; /* Construct the reply message. */ reply = dbus_message_new_method_return (message->message); if (! reply) goto enomem; dbus_message_iter_init_append (reply, &iter); enomem: __attribute__ ((unused)); } while (! reply); /* Send the reply, appending it to the outgoing queue. */ NIH_MUST (dbus_connection_send (message->connection, reply, NULL)); dbus_message_unref (reply); return DBUS_HANDLER_RESULT_HANDLED; }
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; }
void test_write_pidfile (void) { FILE *f; NihError *err; char dirname[PATH_MAX], filename[PATH_MAX], tmpname[PATH_MAX]; int ret; TEST_FUNCTION ("nih_main_write_pidfile"); TEST_FILENAME (dirname); mkdir (dirname, 0755); strcpy (filename, dirname); strcat (filename, "/test.pid"); strcpy (tmpname, dirname); strcat (tmpname, "/.test.pid.tmp"); nih_main_set_pidfile (filename); /* Check that we can write a pid to the file, and have it appaer * on disk where we expect. */ TEST_FEATURE ("with successful write"); ret = nih_main_write_pidfile (1234); TEST_EQ (ret, 0); f = fopen (filename, "r"); TEST_FILE_EQ (f, "1234\n"); fclose (f); /* Check that we can overwrite an existing pid file with a new * value. */ TEST_FEATURE ("with overwrite of existing pid"); ret = nih_main_write_pidfile (5678); TEST_EQ (ret, 0); f = fopen (filename, "r"); TEST_FILE_EQ (f, "5678\n"); fclose (f); /* Check that an error writing to the temporary file does not result * in the replacement of the existing file and does not result in * the unlinking of the temporary file. */ TEST_FEATURE ("with failure to write to temporary file"); f = fopen (tmpname, "w"); fclose (f); chmod (tmpname, 0000); ret = nih_main_write_pidfile (1234); TEST_LT (ret, 0); err = nih_error_get (); TEST_EQ (err->number, EACCES); nih_free (err); f = fopen (filename, "r"); TEST_FILE_EQ (f, "5678\n"); fclose (f); TEST_EQ (chmod (tmpname, 0644), 0); nih_main_unlink_pidfile (); unlink (tmpname); rmdir (dirname); nih_main_set_pidfile (NULL); }
void test_expand (void) { NihError *error; char *env[7], *str; TEST_FUNCTION ("environ_expand"); env[0] = "FOO=frodo"; env[1] = "BAR=bilbo"; env[2] = "BAZ=xx"; env[3] = "HOBBIT=FOO"; env[4] = "NULL="; env[5] = "DOH=oops"; env[6] = NULL; nih_error_push_context(); nih_error_pop_context (); /* Check that we can expand a string containing no expansion. */ TEST_FEATURE ("with no expansion"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "this is a test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "this is a test"); nih_free (str); } /* Check that we can expand a simple string containing a reference * from the environment, with the reference replaced by the environment * variable value. */ TEST_FEATURE ("with simple expansion"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "this is a $FOO test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "this is a frodo test"); nih_free (str); } /* Check that we can expand a simple string containing a reference * from the environment that is smaller than the reference, with the * reference replaced by the environment variable value. */ TEST_FEATURE ("with simple expansion of smaller value"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "this is a $BAZ test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "this is a xx test"); nih_free (str); } /* Check that we can expand a simple string containing a reference * from the environment that is exactly the same size as the * reference, with the reference replaced by the environment variable * value. */ TEST_FEATURE ("with simple expansion of same size value"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "this is a $DOH test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "this is a oops test"); nih_free (str); } /* Check that we can expand a string containing multiple simple * references, with each replaced by the variable value. */ TEST_FEATURE ("with multiple simple expansions"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "test $FOO $BAR$BAZ", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "test frodo bilboxx"); nih_free (str); } /* Check that we can expand a string containing a bracketed * reference, allowing it to nestle against other alphanumerics. */ TEST_FEATURE ("with simple bracketed expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${BAR}test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "bilbotest"); nih_free (str); } /* Check that we can expand a string containing multiple bracketed * references, allowing it to nestle against other alphanumerics. */ TEST_FEATURE ("with multiple simple bracketed expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${BAR}${FOO}test${BAZ}", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "bilbofrodotestxx"); nih_free (str); } /* Check that simple expressions may appear within bracketed * expressions, causing them to be evaluted and the evalution * serving as the reference. */ TEST_FEATURE ("with simple expression inside bracketed expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${$HOBBIT} baggins", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "frodo baggins"); nih_free (str); } /* Check that bracketed expressions may appear within bracketed * expressions. */ TEST_FEATURE ("with bracketed expression inside bracketed expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${${HOBBIT}} baggins", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "frodo baggins"); nih_free (str); } /* Check that we can substitute a default value if the variable * we were after was unset. */ TEST_FEATURE ("with bracketed default expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${MEEP-a }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "a test"); nih_free (str); } /* Check that a default expression uses the environment value if * it is actually set. */ TEST_FEATURE ("with bracketed default expression for set variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${BAZ-a }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "xxtest"); nih_free (str); } /* Check that a default expression uses the environment value if * it is actually set, even if it is NULL. */ TEST_FEATURE ("with bracketed default expression for null variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${NULL-a }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "test"); nih_free (str); } /* Check that we can substitute a default value if the variable * we were after was unset (or null). */ TEST_FEATURE ("with bracketed default or null expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${MEEP:-a }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "a test"); nih_free (str); } /* Check that a default or null expression uses the environment value * if it is actually set and not null. */ TEST_FEATURE ("with bracketed default or null expression for set variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${BAZ:-a }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "xxtest"); nih_free (str); } /* Check that we can substitute a default value if the variable * we were after was null. */ TEST_FEATURE ("with bracketed default or null expression for null variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${NULL:-a }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "a test"); nih_free (str); } /* Check that we don't substitute an alternate value if the * variable we were after was unset. */ TEST_FEATURE ("with bracketed alternate expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${MEEP+good }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "test"); nih_free (str); } /* Check that we use the alternate value if the environment variable * is actually set. */ TEST_FEATURE ("with bracketed alternate expression for set variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${BAZ+good }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "good test"); nih_free (str); } /* Check that we use the alternate value if the environment variable * is set, even if it is NULL. */ TEST_FEATURE ("with bracketed alternate expression for null variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${NULL+good }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "good test"); nih_free (str); } /* Check that we don't substitute an alternate value if the * variable we were after was unset (or null). */ TEST_FEATURE ("with bracketed alternate or null expression"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${MEEP:+good }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "test"); nih_free (str); } /* Check that we use the alternate value if the environment variable * is actually set and not null. */ TEST_FEATURE ("with bracketed alternate or null expression for set variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${BAZ:+good }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "good test"); nih_free (str); } /* Check that we don't substitute an alternate value if the * variable we were after was set, but was null. */ TEST_FEATURE ("with bracketed alternate or null expression for null variable"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${NULL:+good }test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "test"); nih_free (str); } /* Check that references on either side of an expression are * expanded before evaluation. */ TEST_FEATURE ("with references in bracketed expression argument"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${$BAZ:-${$HOBBIT}}test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "frodotest"); nih_free (str); } /* Check that a literal dollar sign with no following text is * treated just as a dollar sign. */ TEST_FEATURE ("with dollar sign in whitespace"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "this is a $ test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "this is a $ test"); nih_free (str); } /* Check that a literal dollar sign in text can be followed by empty * brackets to be just as a dollar sign. */ TEST_FEATURE ("with bracketed dollar sign"); TEST_ALLOC_FAIL { str = environ_expand (NULL, "${}test", env); if (test_alloc_failed) { TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENOMEM); nih_free (error); continue; } TEST_EQ_STR (str, "$test"); nih_free (str); } /* Check that attempting to expand an unknown variable results in * an error being raised. */ TEST_FEATURE ("with simple expansion of unknown variable"); str = environ_expand (NULL, "this is a $WIBBLE test", env); TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM); nih_free (error); /* Check that attempting to expand an unknown variable results in * an error being raised. */ TEST_FEATURE ("with bracketed expansion of unknown variable"); str = environ_expand (NULL, "this is a ${WIBBLE} test", env); TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM); nih_free (error); /* Check that attempting to expand an unknown variable results in * an error being raised. */ TEST_FEATURE ("with expansion of unknown variable within expression name"); str = environ_expand (NULL, "this is a ${$WIBBLE:-$FOO} test", env); TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM); nih_free (error); /* Check that attempting to expand an unknown variable results in * an error being raised. */ TEST_FEATURE ("with expansion of unknown variable within expression argument"); str = environ_expand (NULL, "this is a ${$FOO:-$WIBBLE} test", env); TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM); nih_free (error); /* Check that inventing a new operator results in an error * being raised. */ TEST_FEATURE ("with unknown operator in expression"); str = environ_expand (NULL, "this is a ${$FOO:!$BAR test", env); TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENVIRON_EXPECTED_OPERATOR); nih_free (error); /* Check that forgetting to close a brace results in an error * being raised. */ TEST_FEATURE ("with missing close brace after expression"); str = environ_expand (NULL, "this is a ${$FOO:-$BAR test", env); TEST_EQ_P (str, NULL); error = nih_error_get (); TEST_EQ (error->number, ENVIRON_MISMATCHED_BRACES); nih_free (error); }
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; 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; }
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); }