/* * Compile with: * gcc $(pkg-config --libs --cflags dbus-1) -o main main.c */ #include #include #include #include #include #include static dbus_bool_t _dbus_asv_add_fixed_array(DBusMessageIter *arr_iter, const char *key, char element_type, const void *value, int n_elements) { const char type[] = { DBUS_TYPE_ARRAY, element_type, 0 }; DBusMessageIter entry_iter; DBusMessageIter var_iter; DBusMessageIter array_iter; if (!dbus_message_iter_open_container(arr_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter)) return false; if (!dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key)) { dbus_message_iter_abandon_container (arr_iter, &entry_iter); return false; } if (!dbus_message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, type, &var_iter)) { dbus_message_iter_abandon_container(arr_iter, &entry_iter); return false; } if (!dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, &type[1], &array_iter)) { dbus_message_iter_abandon_container(&entry_iter, &var_iter); dbus_message_iter_abandon_container(arr_iter, &entry_iter); return false; } if (!dbus_message_iter_append_fixed_array(&array_iter, element_type, &value, n_elements)) { dbus_message_iter_abandon_container (&var_iter, &array_iter); dbus_message_iter_abandon_container(&entry_iter, &var_iter); dbus_message_iter_abandon_container(arr_iter, &entry_iter); return false; } if (!dbus_message_iter_close_container (&var_iter, &array_iter)) { dbus_message_iter_abandon_container(&entry_iter, &var_iter); dbus_message_iter_abandon_container(arr_iter, &entry_iter); return false; } /* Close the a{sv} container */ if (!dbus_message_iter_close_container(&entry_iter, &var_iter)) { dbus_message_iter_abandon_container(arr_iter, &entry_iter); return false; } if (!dbus_message_iter_close_container(arr_iter, &entry_iter)) return false; return true; } /** * Call a method on a remote object */ static void _query(int param) { const char *pid_str = "PIDs"; const char *name = "my_scope_test"; const char *mode = "fail"; DBusMessage* msg; DBusMessageIter args_itr, cont_itr, pid_itr, dict_itr; DBusConnection* conn; DBusError err; DBusPendingCall* pending; int ret, pids[10], npids, zero = 0; printf("Calling remote method with %d\n", param); dbus_error_init(&err); /* Connect to the system bus and check for errors. */ conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { fprintf(stderr, "Connection Error (%s)\n", err.message); dbus_error_free(&err); } if (!conn) exit(1); /* * Ask the bus to assign the given name to our connection. * ]$ busctl --user|grep main * :1.481 148274 main user1 :1.481 user@1000.service - - * my.test.caller d 148274 main user1 :1.481 user@1000.service - - */ ret = dbus_bus_request_name(conn, "my.test.caller", DBUS_NAME_FLAG_REPLACE_EXISTING , &err); if (dbus_error_is_set(&err)) { fprintf(stderr, "Name Error (%s)\n", err.message); dbus_error_free(&err); } if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) exit(1); /* * StartTransientUnit() may be used to create and start a transient * unit, which will be released as soon as it is not running or * referenced anymore or the system is rebooted. * * Parameters: * * - name: is the unit name including suffix, and must be unique. * - mode: replace, fail, isolate, ignore-dependencies, * ignore-requirements. * * 'replace' the call will start the unit and its dependencies, * possibly replacing already queued jobs that conflict with * this. * * 'fail' the call will start the unit and its dependencies, * but will fail if this would change an already queued job. * * 'isolate' the call will start the unit in question and * terminate all units that aren't dependencies of it. * * 'ignore-dependencies' it will start a unit but ignore all its * dependencies. * * 'ignore-requirements' it will start a unit but only ignore * the requirement dependencies. * * It is not recommended to make use of the latter two options. * * - properties: array of property name and value pairs for the unit. * - aux is currently unused and should be passed as empty array. * - Returns the newly created job object. * * StartTransientUnit(in s name, * in s mode, * in a(sv) properties, * in a(sa(sv)) aux, * out o job); * Example: * ]# busctl call * org.freedesktop.systemd1 # dbus service name * /org/freedesktop/systemd1 # dbus object * org.freedesktop.systemd1.Manager # interface name * StartTransientUnit # method to call * 'ssa(sv)a(sa(sv))' # signature of method * 'SCOPE-NAME.scope' # first argument, name * fail # second argument * 1 # third argument, number of * systemd properties of unit * PIDs # name of first property * au # data type of first * property, (a)rray of * (u)nsigned integers * 1 # count of array * (number of pids) * 14460 # first pid * 0 # fourth argument: array * size 0 (unused parameter) */ /* Create a new method call. */ msg = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartTransientUnit"); if (!msg) { fprintf(stderr, "Cannot create new method call.\n"); exit(1); } /* Append arguments to the message. */ dbus_message_iter_init_append(msg, &args_itr); /* Name */ if (!dbus_message_iter_append_basic(&args_itr, DBUS_TYPE_STRING, &name)) { fprintf(stderr, "Cannot append name %s to msg.\n", name); exit(1); } /* Mode */ if (!dbus_message_iter_append_basic(&args_itr, DBUS_TYPE_STRING, &mode)) { fprintf(stderr, "Cannot append mode %s to msg.\n", mode); exit(1); } /* Properties */ npids = 1; pids[0] = param; /* a(sv) */ if (!_dbus_asv_add_fixed_array(&args_itr, pid_str, DBUS_TYPE_UINT32, &pids, npids)) exit(1); /* Auxiliary units (always must be null) */ if (!dbus_message_iter_open_container(&args_itr, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &cont_itr)) exit(1); if (!dbus_message_iter_append_basic(&cont_itr, DBUS_TYPE_UINT32, &zero)) { fprintf(stderr, "Cannot append zero to msg.\n"); exit(1); } dbus_message_iter_close_container(&args_itr, &cont_itr); /* Send message and get a handle for a reply. */ if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { fprintf(stderr, "Failed to send message.\n"); exit(1); } if (!pending) { fprintf(stderr, "Reply 'pending' object is null.\n"); exit(1); } dbus_connection_flush(conn); /* Wait for reply */ printf("Request Sent... blocking for reply.\n"); dbus_message_unref(msg); dbus_pending_call_block(pending); msg = dbus_pending_call_steal_reply(pending); printf("\n-- Reply received --\n"); if (!msg) { fprintf(stderr, "Reply msg is null.\n"); exit(1); } dbus_pending_call_unref(pending); /* Manage response message. */ int rc = 0; char *rcstr = NULL; bool stat; dbus_uint32_t level; /* Read the parameters. */ if (!dbus_message_iter_init(msg, &args_itr)) { fprintf(stderr, "Reply message has no arguments.\n"); } else { rc = dbus_message_iter_get_arg_type(&args_itr); if (rc == DBUS_TYPE_BOOLEAN) { dbus_message_iter_get_basic(&args_itr, &stat); printf("Test: ret. arg is type boolean: %d\n", stat); } if (rc == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&args_itr, &rcstr); printf("Test: ret. arg is type string: %s\n", rcstr); } } /* If more arguments. */ if (dbus_message_iter_next(&args_itr)) { if (dbus_message_iter_get_arg_type(&args_itr) != DBUS_TYPE_UINT32) fprintf(stderr, "Test: argument is not int.. implement me.\n"); else { dbus_message_iter_get_basic(&args_itr, &level); printf("Got Reply: %d, %d\n", stat, level); } } dbus_message_unref(msg); } int main(int argc, char** argv) { /* busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1 * busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager * * A service is a program that offers some IPC API on a bus. * An object path is an identifier for an object on a specific service. * An object has one or more interfaces (signals, methods and properties). * A method is a function implemented in the object * * Service name: org.freedesktop.systemd1 * Object path: /org/freedesktop/systemd1 * Interface: org.freedesktop.systemd1.Manager * Method: .ListUnits */ /* Connect to the system bus */ /* Check if transient unit is started */ /* Call start transient unit */ /* Check status */ printf("-- Starting query --\n"); _query(atoi(argv[1])); return 0; }