static void dump_services( int fd ) { unsigned u ; /* * Dump the current configuration (services + defaults) */ Sprint( fd, "Services + defaults:\n" ) ; sc_dump( DEFAULTS( ps ), fd, 0, TRUE ) ; for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) svc_dump( SP( pset_pointer( SERVICES( ps ), u ) ), fd ) ; }
/* * Release the specified connection. * Certain actions may be performed before doing this: * - drain of a single UDP packet if the socket type is SOCK_DGRAM */ void conn_free( connection_s *cp, int release_mem ) { struct service *sp ; if( cp == NULL ) return; if( debug.on ) msg( LOG_INFO, "conn_free", "freeing connection") ; sp = cp->co_sp ; if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) ) drain( cp->co_descriptor ) ; if ( SVC_RELE( sp ) == 0 ) { pset_remove( SERVICES( ps ), sp ) ; svc_release( sp ); } cp->co_sp = NULL; if ( CONN_DESCRIPTOR( cp ) > 0 ) CONN_CLOSE( cp ) ; CLEAR( *cp ) ; if (release_mem) { FREE_CONN( cp ) ; } }
static gboolean handle_list_services (CockpitServices *object, GDBusMethodInvocation *invocation) { Services *services = SERVICES (object); if (services->systemd == NULL) { g_dbus_method_invocation_return_error (invocation, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "systemd not running"); return TRUE; } ListServicesData *data = g_new0(ListServicesData, 1); data->services = services; data->invocation = invocation; g_dbus_proxy_call (services->systemd, "ListUnits", NULL, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, on_list_units_done, data); return TRUE; }
static void services_finalize (GObject *object) { Services *services = SERVICES (object); g_hash_table_destroy (services->delayed_unit_news); if (G_OBJECT_CLASS (services_parent_class)->finalize != NULL) G_OBJECT_CLASS (services_parent_class)->finalize (object); }
static gboolean handle_get_service_info (CockpitServices *object, GDBusMethodInvocation *invocation, const gchar *arg_name) { Services *services = SERVICES (object); if (services->systemd == NULL) { g_dbus_method_invocation_return_error (invocation, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "systemd not running"); return TRUE; } GetServiceInfoData *data = g_new0(GetServiceInfoData, 1); data->services = services; data->invocation = invocation; gchar *t = strchr ((gchar *)arg_name, '@'); gchar *s = strrchr ((gchar *)arg_name, '.'); if (t > 0 && t + 1 == s) { // A template data->name = g_strdup (arg_name); g_dbus_proxy_call (services->systemd, "GetUnitFileState", g_variant_new ("(s)", arg_name), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, on_get_unit_file_state_done, data); } else g_dbus_proxy_call (services->systemd, "LoadUnit", g_variant_new ("(s)", arg_name), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, on_load_unit_done, data); return TRUE; }
/* * This function closes all service descriptors. This should be called * for all child processes that fork, but do not exec. This includes * redirect, builtins, and tcpmux. The close on exec flag takes care of * child processes that call exec. Without calling this, the listening * fd's are not closed and reconfig will fail. */ void close_all_svc_descriptors(void) { psi_h iter ; struct service *osp ; /* Have to close all other descriptors here */ iter = psi_create( SERVICES( ps ) ) ; if ( iter == NULL ) { out_of_memory( "close_all_svc_descriptors" ) ; exit( 1 ); } for ( osp = SP( psi_start( iter ) ) ; osp ; osp = SP( psi_next( iter ) ) ) (void) Sclose( SVC_FD( osp ) ) ; psi_destroy( iter ) ; }
static void services_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { Services *services = SERVICES (object); switch (prop_id) { case PROP_DAEMON: g_value_set_object (value, services_get_daemon (services)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void services_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { Services *services = SERVICES (object); switch (prop_id) { case PROP_DAEMON: g_assert (services->daemon == NULL); services->daemon = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean handle_service_action (CockpitServices *object, GDBusMethodInvocation *invocation, const gchar *arg_name, const gchar *arg_action) { GError *error; Services *services = SERVICES (object); const gchar *argv[6]; int i; gint status; const gchar *method = arg_action; gboolean force = FALSE; if (g_str_has_prefix (arg_action, "force-")) { force = TRUE; method = arg_action + strlen ("force-"); } i = 0; argv[i++] = "pkexec"; argv[i++] = "systemctl"; if (force) argv[i++] = "--force"; argv[i++] = method; argv[i++] = arg_name; argv[i++] = NULL; error = NULL; if (g_spawn_sync (NULL, (gchar**)argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &status, &error)) g_spawn_check_exit_status (status, &error); if (error) end_invocation_take_gerror (invocation, error); else cockpit_services_complete_service_action (COCKPIT_SERVICES (services), invocation); return TRUE; }
static void consistency_check( enum check_type type ) { int fd ; fd_set socket_mask_copy ; unsigned u ; int errors ; unsigned total_running_servers = 0 ; unsigned total_retry_servers = 0 ; unsigned error_count = 0 ; bool_int service_count_check_failed = FALSE ; const char *func = "consistency_check" ; socket_mask_copy = ps.rws.socket_mask ; for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { register struct service *sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; char *sid = SVC_ID( sp ) ; unsigned running_servers ; unsigned retry_servers ; error_count += refcount_check( sp, &running_servers, &retry_servers ) ; if ( SVC_IS_AVAILABLE( sp ) || SVC_IS_DISABLED ( sp ) ) { /* * In this case, there may be some servers running */ if ( FD_ISSET( SVC_FD( sp ), &socket_mask_copy ) ) { if ( SVC_IS_DISABLED( sp ) ) { msg( LOG_ERR, func, "fd of disabled service %s still in socket mask", sid ) ; error_count++ ; } FD_CLR( SVC_FD( sp ), &socket_mask_copy ) ; } error_count += thread_check( sp, running_servers, retry_servers ) ; errors = service_count_check( sp, running_servers, retry_servers ) ; if ( ! errors && ! service_count_check_failed ) { total_retry_servers += retry_servers ; total_running_servers += running_servers ; } if ( errors ) { service_count_check_failed = TRUE ; error_count += errors ; } if ( SVC_IS_DISABLED( sp ) && SVC_RUNNING_SERVERS( sp ) == 0 ) { msg( LOG_ERR, func, "disabled service %s has 0 running servers\n", sid ) ; error_count++ ; continue ; } } else { msg( LOG_ERR, func, "service %s not started", SVC_ID( sp ) ) ; error_count++ ; } } if ( ! service_count_check_failed ) { if ( total_running_servers != pset_count( SERVERS( ps ) ) ) { msg( LOG_ERR, func, "total running servers (%d) != number of running servers (%d)", total_running_servers, pset_count( SERVERS( ps ) ) ) ; error_count++ ; } if ( total_retry_servers != pset_count( RETRIES( ps ) ) ) { msg( LOG_ERR, func, "total retry servers (%d) != number of retry servers (%d)", total_retry_servers, pset_count( RETRIES( ps ) ) ) ; error_count++ ; } } /* * Check if there are any descriptors set in socket_mask_copy */ for ( fd = 0 ; fd < ps.ros.max_descriptors ; fd++ ) if ( FD_ISSET( fd, &socket_mask_copy ) && ((fd != signals_pending[0]) && fd != signals_pending[1])) { msg( LOG_ERR, func, "descriptor %d set in socket mask but there is no service for it", fd ) ; error_count++ ; } if ( error_count != 0 ) msg( LOG_WARNING, func, "Consistency check detected %d errors", error_count ) ; else if ( type == USER_REQUESTED || debug.on ) msg( LOG_INFO, func, "Consistency check passed" ) ; if( type == PERIODIC ) if ( xtimer_add( periodic_check, ps.ros.cc_interval ) == -1 ) msg( LOG_ERR, func, "Failed to start consistency timer" ) ; }
static gboolean handle_service_action (CockpitServices *object, GDBusMethodInvocation *invocation, const gchar *arg_name, const gchar *arg_action) { Services *services = SERVICES (object); if (!auth_check_sender_role (invocation, COCKPIT_ROLE_ADMIN)) return TRUE; if (services->systemd == NULL) { g_dbus_method_invocation_return_error (invocation, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "systemd not running"); return TRUE; } const gchar *method = NULL; int flags = 0; for (int i = 0; action_methods[i].action; i++) { if (strcmp (action_methods[i].action, arg_action) == 0) { method = action_methods[i].method; flags = action_methods[i].flags; break; } } if (method == NULL) { g_dbus_method_invocation_return_error (invocation, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "Unsupported action: %s", arg_action); return TRUE; } const gchar *mode = (flags & ISOLATE) ? "isolate" : "replace"; ServiceActionData *data = g_new0(ServiceActionData, 1); data->services = services; data->invocation = invocation; data->flags = flags; GVariant *args; if (flags & FOR_FILES) { const gchar *files[] = { arg_name, NULL }; if ((flags & FORCE) || (flags & NO_FORCE)) args = g_variant_new ("(^asbb)", files, FALSE, flags & FORCE); else args = g_variant_new ("(^asb)", files, FALSE); } else args = g_variant_new ("(ss)", arg_name, mode); g_dbus_proxy_call (services->systemd, method, args, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, on_service_action_done, data); return TRUE; }
static void services_constructed (GObject *_object) { Services *services = SERVICES (_object); GDBusConnection *connection; services->systemd = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", NULL, NULL); connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); if (connection) { g_dbus_connection_signal_subscribe (connection, "org.freedesktop.systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged", "/org/freedesktop/systemd1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, on_unit_files_changed_signal, services, NULL); g_dbus_connection_signal_subscribe (connection, "org.freedesktop.systemd1", "org.freedesktop.DBus.Properties", "PropertiesChanged", NULL, NULL, G_DBUS_SIGNAL_FLAGS_NONE, on_systemd_properties_changed, services, NULL); g_dbus_connection_signal_subscribe (connection, "org.freedesktop.systemd1", "org.freedesktop.systemd1.Manager", "UnitNew", "/org/freedesktop/systemd1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, on_unit_new_signal, services, NULL); g_dbus_connection_signal_subscribe (connection, "org.freedesktop.systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved", "/org/freedesktop/systemd1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, on_unit_removed_signal, services, NULL); } if (services->systemd) g_dbus_proxy_call (services->systemd, "Subscribe", NULL, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, on_subscribe_done, NULL); services->delayed_unit_news = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_delayed_unit_new); if (G_OBJECT_CLASS (services_parent_class)->constructed != NULL) G_OBJECT_CLASS (services_parent_class)->constructed (_object); }
static void tcpmux_handler( const struct server *serp ) { char svc_name[ BUFFER_SIZE ] ; int cc ; int descriptor = SERVER_FD( serp ) ; const struct service *svc = SERVER_SERVICE( serp ) ; unsigned u; struct service *sp = NULL; struct server server, *nserp; struct service_config *scp = NULL; close_all_svc_descriptors(); /* Read in the name of the service in the format "svc_name\r\n". * * XXX: should loop on partial reads (could probably use Sread() if * it wasn't thrown out of xinetd source code a few revisions back). */ do { cc = read( descriptor, svc_name, sizeof( svc_name ) ) ; } while (cc == -1 && errno == EINTR); if ( cc <= 0 ) { msg(LOG_ERR, "tcpmux_handler", "read failed"); exit(0); } if ( ( cc <= 2 ) || ( ( svc_name[cc - 1] != '\n' ) || ( svc_name[cc - 2] != '\r' ) ) ) { if ( debug.on ) msg(LOG_DEBUG, "tcpmux_handler", "Invalid service name format."); exit(0); } svc_name[cc - 2] = '\0'; /* Remove \r\n for compare */ if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Input (%d bytes) %s as service name.", cc, svc_name); } /* Search the services for the a match on name. */ for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( strcasecmp( svc_name, SC_NAME( SVC_CONF( sp ) ) ) == 0 ) { /* Found the pointer. Validate its type. */ scp = SVC_CONF( sp ); /* if ( ! SVC_IS_MUXCLIENT( sp ) ) { if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Non-tcpmux service name: %s.", svc_name); } exit(0); } */ /* Send the accept string if we're a PLUS (+) client. */ if ( SVC_IS_MUXPLUSCLIENT( sp ) ) { if ( Swrite( descriptor, TCPMUX_ACK, sizeof( TCPMUX_ACK ) ) != sizeof( TCPMUX_ACK ) ) { msg(LOG_ERR, "tcpmux_handler", "Ack write failed for %s.", svc_name); exit(0); } } break; /* Time to get on with the service */ } continue; /* Keep looking */ } if ( u >= pset_count( SERVICES( ps ) ) ) { if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Service name %s not found.", svc_name); } exit(0); } if( SVC_WAITS( svc ) ) /* Service forks, so close it */ Sclose(descriptor); server.svr_sp = sp; server.svr_conn = SERVER_CONNECTION(serp); nserp = server_alloc(&server); if( SC_IS_INTERNAL( scp ) ) { SC_INTERNAL(scp, nserp); } else { exec_server(nserp); } }