77
88#include "alloc-util.h"
99#include "bus-error.h"
10+ #include "bus-unit-util.h"
1011#include "bus-util.h"
1112#include "def.h"
1213#include "dirent-util.h"
@@ -39,6 +40,8 @@ static bool arg_reload = true;
3940static bool arg_cat = false;
4041static BusTransport arg_transport = BUS_TRANSPORT_LOCAL ;
4142static const char * arg_host = NULL ;
43+ static bool arg_enable = false;
44+ static bool arg_now = false;
4245
4346static int determine_image (const char * image , bool permit_non_existing , char * * ret ) {
4447 int r ;
@@ -388,6 +391,204 @@ static int print_changes(sd_bus_message *m) {
388391 return 0 ;
389392}
390393
394+ static int maybe_enable_disable (sd_bus * bus , const char * path , bool enable ) {
395+ _cleanup_ (sd_bus_message_unrefp ) sd_bus_message * m = NULL , * reply = NULL ;
396+ _cleanup_ (sd_bus_error_free ) sd_bus_error error = SD_BUS_ERROR_NULL ;
397+ _cleanup_strv_free_ char * * names = NULL ;
398+ UnitFileChange * changes = NULL ;
399+ size_t n_changes = 0 ;
400+ int r ;
401+
402+ if (!arg_enable )
403+ return 0 ;
404+
405+ names = strv_new (path , NULL );
406+ if (!names )
407+ return log_oom ();
408+
409+ r = sd_bus_message_new_method_call (
410+ bus ,
411+ & m ,
412+ "org.freedesktop.systemd1" ,
413+ "/org/freedesktop/systemd1" ,
414+ "org.freedesktop.systemd1.Manager" ,
415+ enable ? "EnableUnitFiles" : "DisableUnitFiles" );
416+ if (r < 0 )
417+ return bus_log_create_error (r );
418+
419+ r = sd_bus_message_append_strv (m , names );
420+ if (r < 0 )
421+ return bus_log_create_error (r );
422+
423+ r = sd_bus_message_append (m , "b" , arg_runtime );
424+ if (r < 0 )
425+ return bus_log_create_error (r );
426+
427+ if (enable ) {
428+ r = sd_bus_message_append (m , "b" , false);
429+ if (r < 0 )
430+ return bus_log_create_error (r );
431+ }
432+
433+ r = sd_bus_call (bus , m , 0 , & error , & reply );
434+ if (r < 0 )
435+ return log_error_errno (r , "Failed to %s the portable service %s: %s" ,
436+ enable ? "enable" : "disable" , path , bus_error_message (& error , r ));
437+
438+ if (enable ) {
439+ r = sd_bus_message_skip (reply , "b" );
440+ if (r < 0 )
441+ return bus_log_parse_error (r );
442+ }
443+ (void ) bus_deserialize_and_dump_unit_file_changes (reply , arg_quiet , & changes , & n_changes );
444+
445+ return 0 ;
446+ }
447+
448+ static int maybe_start_stop (sd_bus * bus , const char * path , bool start ) {
449+ _cleanup_ (sd_bus_message_unrefp ) sd_bus_message * reply = NULL ;
450+ _cleanup_ (sd_bus_error_free ) sd_bus_error error = SD_BUS_ERROR_NULL ;
451+ char * name = (char * )basename (path ), * job = NULL ;
452+ int r ;
453+
454+ if (!arg_now )
455+ return 0 ;
456+
457+ r = sd_bus_call_method (
458+ bus ,
459+ "org.freedesktop.systemd1" ,
460+ "/org/freedesktop/systemd1" ,
461+ "org.freedesktop.systemd1.Manager" ,
462+ start ? "StartUnit" : "StopUnit" ,
463+ & error ,
464+ & reply ,
465+ "ss" , name , "replace" );
466+ if (r < 0 )
467+ return log_error_errno (r , "Failed to %s the portable service %s: %s" ,
468+ start ? "start" : "stop" ,
469+ path ,
470+ bus_error_message (& error , r ));
471+
472+ r = sd_bus_message_read (reply , "o" , & job );
473+ if (r < 0 )
474+ return bus_log_parse_error (r );
475+
476+ if (!arg_quiet )
477+ log_info ("Queued %s to %s portable service %s." , job , start ? "start" : "stop" , name );
478+
479+ return 0 ;
480+ }
481+
482+ static int maybe_enable_start (sd_bus * bus , sd_bus_message * reply ) {
483+ int r ;
484+
485+ if (!arg_enable && !arg_now )
486+ return 0 ;
487+
488+ r = sd_bus_message_rewind (reply , true);
489+ if (r < 0 )
490+ return r ;
491+ r = sd_bus_message_enter_container (reply , 'a' , "(sss)" );
492+ if (r < 0 )
493+ return bus_log_parse_error (r );
494+
495+ for (;;) {
496+ char * type , * path , * source ;
497+
498+ r = sd_bus_message_read (reply , "(sss)" , & type , & path , & source );
499+ if (r < 0 )
500+ return bus_log_parse_error (r );
501+ if (r == 0 )
502+ break ;
503+
504+ if (STR_IN_SET (type , "symlink" , "copy" ) && ENDSWITH_SET (path , ".service" , ".target" , ".socket" )) {
505+ (void ) maybe_enable_disable (bus , path , true);
506+ (void ) maybe_start_stop (bus , path , true);
507+ }
508+ }
509+
510+ r = sd_bus_message_exit_container (reply );
511+ if (r < 0 )
512+ return r ;
513+
514+ return 0 ;
515+ }
516+
517+ static int maybe_stop_disable (sd_bus * bus , char * image , char * argv []) {
518+ _cleanup_ (sd_bus_message_unrefp ) sd_bus_message * m = NULL , * reply = NULL ;
519+ _cleanup_ (sd_bus_error_free ) sd_bus_error error = SD_BUS_ERROR_NULL ;
520+ _cleanup_strv_free_ char * * matches = NULL ;
521+ int r ;
522+
523+ if (!arg_enable && !arg_now )
524+ return 0 ;
525+
526+ r = determine_matches (argv [1 ], argv + 2 , true, & matches );
527+ if (r < 0 )
528+ return r ;
529+
530+ r = sd_bus_message_new_method_call (
531+ bus ,
532+ & m ,
533+ "org.freedesktop.portable1" ,
534+ "/org/freedesktop/portable1" ,
535+ "org.freedesktop.portable1.Manager" ,
536+ "GetImageMetadata" );
537+ if (r < 0 )
538+ return bus_log_create_error (r );
539+
540+ r = sd_bus_message_append (m , "s" , image );
541+ if (r < 0 )
542+ return bus_log_create_error (r );
543+
544+ r = sd_bus_message_append_strv (m , matches );
545+ if (r < 0 )
546+ return bus_log_create_error (r );
547+
548+ r = sd_bus_call (bus , m , 0 , & error , & reply );
549+ if (r < 0 )
550+ return log_error_errno (r , "Failed to inspect image metadata: %s" , bus_error_message (& error , r ));
551+
552+ r = sd_bus_message_skip (reply , "say" );
553+ if (r < 0 )
554+ return bus_log_parse_error (r );
555+
556+ r = sd_bus_message_enter_container (reply , 'a' , "{say}" );
557+ if (r < 0 )
558+ return bus_log_parse_error (r );
559+
560+ for (;;) {
561+ const char * name ;
562+
563+ r = sd_bus_message_enter_container (reply , 'e' , "say" );
564+ if (r < 0 )
565+ return bus_log_parse_error (r );
566+ if (r == 0 )
567+ break ;
568+
569+ r = sd_bus_message_read (reply , "s" , & name );
570+ if (r < 0 )
571+ return bus_log_parse_error (r );
572+
573+ r = sd_bus_message_skip (reply , "ay" );
574+ if (r < 0 )
575+ return bus_log_parse_error (r );
576+
577+ r = sd_bus_message_exit_container (reply );
578+ if (r < 0 )
579+ return bus_log_parse_error (r );
580+
581+ (void ) maybe_start_stop (bus , name , false);
582+ (void ) maybe_enable_disable (bus , name , false);
583+ }
584+
585+ r = sd_bus_message_exit_container (reply );
586+ if (r < 0 )
587+ return bus_log_parse_error (r );
588+
589+ return 0 ;
590+ }
591+
391592static int attach_image (int argc , char * argv [], void * userdata ) {
392593 _cleanup_ (sd_bus_message_unrefp ) sd_bus_message * m = NULL , * reply = NULL ;
393594 _cleanup_ (sd_bus_error_free ) sd_bus_error error = SD_BUS_ERROR_NULL ;
@@ -439,6 +640,9 @@ static int attach_image(int argc, char *argv[], void *userdata) {
439640 (void ) maybe_reload (& bus );
440641
441642 print_changes (reply );
643+
644+ (void ) maybe_enable_start (bus , reply );
645+
442646 return 0 ;
443647}
444648
@@ -459,6 +663,8 @@ static int detach_image(int argc, char *argv[], void *userdata) {
459663
460664 (void ) polkit_agent_open_if_enabled (arg_transport , arg_ask_password );
461665
666+ (void ) maybe_stop_disable (bus , image , argv );
667+
462668 r = sd_bus_call_method (
463669 bus ,
464670 "org.freedesktop.portable1" ,
@@ -764,7 +970,8 @@ static int help(int argc, char *argv[], void *userdata) {
764970 " list List available portable service images\n"
765971 " attach NAME|PATH [PREFIX...]\n"
766972 " Attach the specified portable service image\n"
767- " detach NAME|PATH Detach the specified portable service image\n"
973+ " detach NAME|PATH [PREFIX...]\n"
974+ " Detach the specified portable service image\n"
768975 " inspect NAME|PATH [PREFIX...]\n"
769976 " Show details of specified portable service image\n"
770977 " is-attached NAME|PATH Query if portable service image is attached\n"
@@ -786,6 +993,10 @@ static int help(int argc, char *argv[], void *userdata) {
786993 " --no-reload Don't reload the system and service manager\n"
787994 " --cat When inspecting include unit and os-release file\n"
788995 " contents\n"
996+ " --enable Immediately enable/disable the portable service\n"
997+ " after attach/detach\n"
998+ " --now Immediately start/stop the portable service after\n"
999+ " attach/before detach\n"
7891000 "\nSee the %s for details.\n"
7901001 , program_invocation_short_name
7911002 , ansi_highlight ()
@@ -807,6 +1018,8 @@ static int parse_argv(int argc, char *argv[]) {
8071018 ARG_RUNTIME ,
8081019 ARG_NO_RELOAD ,
8091020 ARG_CAT ,
1021+ ARG_ENABLE ,
1022+ ARG_NOW ,
8101023 };
8111024
8121025 static const struct option options [] = {
@@ -823,6 +1036,8 @@ static int parse_argv(int argc, char *argv[]) {
8231036 { "runtime" , no_argument , NULL , ARG_RUNTIME },
8241037 { "no-reload" , no_argument , NULL , ARG_NO_RELOAD },
8251038 { "cat" , no_argument , NULL , ARG_CAT },
1039+ { "enable" , no_argument , NULL , ARG_ENABLE },
1040+ { "now" , no_argument , NULL , ARG_NOW },
8261041 {}
8271042 };
8281043
@@ -909,6 +1124,14 @@ static int parse_argv(int argc, char *argv[]) {
9091124 arg_cat = true;
9101125 break ;
9111126
1127+ case ARG_ENABLE :
1128+ arg_enable = true;
1129+ break ;
1130+
1131+ case ARG_NOW :
1132+ arg_now = true;
1133+ break ;
1134+
9121135 case '?' :
9131136 return - EINVAL ;
9141137
@@ -925,7 +1148,7 @@ static int run(int argc, char *argv[]) {
9251148 { "help" , VERB_ANY , VERB_ANY , 0 , help },
9261149 { "list" , VERB_ANY , 1 , VERB_DEFAULT , list_images },
9271150 { "attach" , 2 , VERB_ANY , 0 , attach_image },
928- { "detach" , 2 , 2 , 0 , detach_image },
1151+ { "detach" , 2 , VERB_ANY , 0 , detach_image },
9291152 { "inspect" , 2 , VERB_ANY , 0 , inspect_image },
9301153 { "is-attached" , 2 , 2 , 0 , is_image_attached },
9311154 { "read-only" , 2 , 3 , 0 , read_only_image },
0 commit comments