justin_hanekom

systemd-control.pl

Sep 14th, 2025
981
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 38.43 KB | Source Code | 0 0
  1. #!/usr/bin/env perl
  2.  
  3. # File: systemd-control.pl
  4. # SPDX-License-Identifier: Unlicense
  5.  
  6. # This is free and unencumbered software released into the public domain.
  7. #
  8. # Anyone is free to copy, modify, publish, use, compile, sell, or
  9. # distribute this software, either in source code form or as a compiled
  10. # binary, for any purpose, commercial or non-commercial, and by any
  11. # means.
  12.  
  13. # Tectonics:
  14. #   perltidy -b -pbp -nst -xci systemd-control.pl
  15. #   perlcritic -p perlcriticrc systemd-control.pl
  16. #   podchecker systemd-control.pl
  17.  
  18. package SystemdController;
  19.  
  20. use 5.040;
  21. use strict;
  22. use warnings;
  23. use strictures 2;
  24. use autodie qw( :all );
  25. use version; our $VERSION = version->declare('0.1.0');
  26.  
  27. use File::Path          qw( make_path );
  28. use IPC::System::Simple qw( capture capturex system systemx );
  29. use English             qw( -no_match_vars );
  30. use Getopt::Euclid      qw( :vars );
  31. use IO::Prompt::Tiny    qw( prompt );
  32. use Term::ANSIColor;
  33. use Class::Std;
  34. use Readonly;
  35. use Carp;
  36. use Data::Dumper;
  37.  
  38. # use Smart::Comments;    # comment out to disable smart comments
  39.  
  40. # error codes
  41. Readonly my $GENERAL_ERR         => 1;
  42. Readonly my $INVALID_ARG_ERR     => 3;
  43. Readonly my $READ_WRITE_ERR      => 5;
  44. Readonly my $CMDLINE_ERR         => 64;
  45. Readonly my $OPEN_INPUT_ERR      => 66;
  46. Readonly my $INTERNAL_SW_ERR     => 70;
  47. Readonly my $OS_ERR              => 71;
  48. Readonly my $FILE_MISSING_ERR    => 72;
  49. Readonly my $CANT_CREATE_ERR     => 73;
  50. Readonly my $IO_ERR              => 74;
  51. Readonly my $PERMISSION_ERR      => 77;
  52. Readonly my $CONFIG_ERR          => 78;
  53. Readonly my $CMD_CANNOT_EXEC_ERR => 126;
  54. Readonly my $CMD_NOT_FOUND_ERR   => 127;
  55.  
  56. {
  57.     ### program started...
  58.     ### Euclid has already parsed \$ARGV into \%ARGV...
  59.     ### %ARGV
  60.     ### @ARGV has been consumed by Euclid: @ARGV
  61.  
  62.     Readonly my $UNITS_DIR       => '/etc/systemd/system';
  63.     Readonly my $EXECUTABLES_DIR => '/usr/local/bin';
  64.     Readonly my $ERROR_COLOR     => 'bright_red';
  65.     Readonly my $INFO_COLOR      => 'white';
  66.     Readonly my $STATUS_COLOR    => 'bright_cyan';
  67.     Readonly my $WARNING_COLOR   => 'bright_yellow';
  68.     ### systemd unit file links stored in: $UNITS_DIR
  69.     ### executable file links stored in: $EXECUTABLES_DIR
  70.     ### error colour name              : $ERROR_COLOR
  71.     ### info colour name               : $INFO_COLOR
  72.     ### status colour name             : $STATUS_COLOR
  73.     ### warning colour name            : $WARNING_COLOR
  74.  
  75.     # class attributes
  76.     my %argv_ref_of : ATTR( :init_arg => 'argv_ref', :get => 'argv_ref' );
  77.     my %filenames_ref_of :
  78.         ATTR( :get => 'filenames_ref', :set => 'filenames_ref' );
  79.     my %options_ref_of : ATTR( :get => 'options_ref', :set => 'options_ref' );
  80.     my %is_confirm_of : ATTR( :get => 'is_confirm', :set => 'is_confirm' );
  81.     my %is_interactive_of :
  82.         ATTR( :get => 'is_interactive', :set => 'is_interactive' );
  83.     my %is_verbose_of : ATTR( :get => 'is_verbose', :set => 'is_verbose' );
  84.  
  85.     my $controller = SystemdController->new( { argv_ref => \@ARGV } );
  86.     exit $controller->run();
  87.     ### assert: !'execution should have ended'
  88.  
  89.     # ----------------------------------------------------------------------
  90.     # Name       : START
  91.     # Purpose    : Sets object attributes to default values
  92.     # Returns    : n/a
  93.     # Parameters : $self - reference to this class instance
  94.     #              $id - instance identifier
  95.     #              $args_ref - reference to arguments new() was called with
  96.     # Throws     : no exceptions
  97.     sub START( $self, $id, $args_ref ) {
  98.         ### START() started...
  99.         ### assert: $self
  100.         ### assert: $id
  101.  
  102.         ### filenames defaults to empty list...
  103.         $self->set_filenames_ref( [] )
  104.             if ( !$filenames_ref_of{$id} );
  105.  
  106.         ### $filenames_ref_of{$id}: $filenames_ref_of{$id}
  107.         ### assert: @{$filenames_ref_of{$id}} == 0
  108.  
  109.         ### confirm defaults to 0...
  110.         $self->set_is_confirm(0) if ( !$is_confirm_of{$id} );
  111.  
  112.         ### $is_confirm_of{$id}: $is_confirm_of{$id}
  113.         ### assert: ref($is_confirm_of{$id}) == 0
  114.  
  115.         ### interactive defaults to 0...
  116.         $self->set_is_interactive(0)
  117.             if ( !$is_interactive_of{$id} );
  118.  
  119.         ### $is_interactive_of{$id}: $is_interactive_of{$id}
  120.         ### assert: ref($is_interactive_of{$id}) == 0
  121.  
  122.         ### is_verbose defaults to 0...
  123.         $self->set_is_verbose(0)
  124.             if ( !$is_verbose_of{$id} );
  125.  
  126.         ### $is_verbose_of{$id}: $is_verbose_of{$id}
  127.         ### assert: ref($is_verbose_of{$id}) == 0
  128.  
  129.         ### options defaults to empty hash...
  130.         $self->set_options_ref( {} )
  131.             if ( !$options_ref_of{$id} );
  132.  
  133.         ### $options_ref_of{$id}: $options_ref_of{$id}
  134.         ### assert: keys %{$options_ref_of{$id}} == 0
  135.  
  136.         return $self;
  137.     }    # START
  138.  
  139.     # ----------------------------------------------------------------------
  140.     # Usage      : $controller->run()
  141.     # Purpose    : Runs the systemd controller. Operates on personal systemd
  142.     #              services, timers, and executables
  143.     # Returns    : Program status code
  144.     # Parameters : $self - reference to this class instance
  145.     # Throws     : no exceptions
  146.     # Comments   : If an error occurs, then writes the error message
  147.     #              to the error output.
  148.     #              In this case the program exits with the status code
  149.     #              returned by the code that experienced an error
  150.     sub run($self) {
  151.         ### run() started...
  152.         ### assert: $self
  153.  
  154.         my $opt_ref_of = $self->parse_cmdline();
  155.         ### parse_cmdline returned: $opt_ref_of
  156.  
  157.         ### ensure that UNITS_DIR and EXECUTABLES_DIR exist...
  158.         make_path( $UNITS_DIR,       { mode => 0o775 } );
  159.         make_path( $EXECUTABLES_DIR, { mode => 0o775 } );
  160.  
  161.         ### add symbolic links to files...
  162.         if ( $self->get_options_ref()->{add} ) {
  163.             $self->print_status_if_verbose(
  164.                 "\nAdding symbolic links to files...\n");
  165.  
  166.             $self->add_executable_links();
  167.             $self->add_systemd_links();
  168.         }
  169.  
  170.         ### remove symbolic links to files...
  171.         if ( $self->get_options_ref()->{remove} ) {
  172.             $self->print_status_if_verbose(
  173.                 "\nRemoving symbolic links to files...\n");
  174.             $self->remove_executable_links();
  175.             $self->remove_systemd_links();
  176.         }
  177.  
  178.         ### enable systemd units and executables...
  179.         if ( $self->get_options_ref()->{enable} ) {
  180.             $self->print_status_if_verbose(
  181.                 "\nEnabling systemd units and executables...\n");
  182.             $self->enable_systemd_services();
  183.             $self->enable_systemd_timers();
  184.             $self->enable_executables();
  185.         }
  186.  
  187.         ### list systemd services, timers, and executables...
  188.         if ( $self->get_options_ref()->{list_services} ) {
  189.             $self->print_status_if_verbose("\nListing services...\n");
  190.             $self->list_services();
  191.         }
  192.  
  193.         if ( $self->get_options_ref()->{list_timers} ) {
  194.             $self->print_status_if_verbose("\nListing timers...\n");
  195.             $self->list_timers();
  196.         }
  197.  
  198.         if ( $self->get_options_ref()->{list_executables} ) {
  199.             $self->print_status_if_verbose("\nListing executables...\n");
  200.             $self->list_executables();
  201.         }
  202.  
  203.         return 0;
  204.     }    # run
  205.  
  206.     # ----------------------------------------------------------------------
  207.     # Usage      : %options = $controller->parse_cmdline()
  208.     # Purpose    : Parses any and all command line arguments and/or options
  209.     # Returns    : A hash of the found arguments/options
  210.     # Parameters : $self - reference to this class instance
  211.     # Throws     : Any unanticipated error is returned to the caller
  212.     #              CAVEAT: does not throw errors for unknown options
  213.     # Comments   : If the help, usage, man, or version option are given then
  214.     #              they write their message and exit the program with status
  215.     #              code 0.
  216.     #              If the command line could not be parsed then an error is
  217.     #              written to the error output stream and the program exits
  218.     #              with status code 2.
  219.     sub parse_cmdline($self) {
  220.         ### parse_cmdline() started...
  221.         ### assert: $self
  222.  
  223.         ### parsing by Euclid has already occurred at this point...
  224.  
  225.         ### transfer the parsed values to a new, normalized, hash...
  226.         my $result_ref = {};
  227.         $result_ref->{add}              = $ARGV_add              ? 1 : 0;
  228.         $result_ref->{confirm}          = $ARGV_confirm          ? 1 : 0;
  229.         $result_ref->{enable}           = $ARGV_enable           ? 1 : 0;
  230.         $result_ref->{interactive}      = $ARGV_interactive      ? 1 : 0;
  231.         $result_ref->{list_services}    = $ARGV_list_services    ? 1 : 0;
  232.         $result_ref->{list_timers}      = $ARGV_list_timers      ? 1 : 0;
  233.         $result_ref->{list_executables} = $ARGV_list_executables ? 1 : 0;
  234.         $result_ref->{verbose}          = $ARGV_verbose          ? 1 : 0;
  235.         $result_ref->{filenames}        = \@ARGV_filename;
  236.  
  237.         ### update instance attributes based on
  238.         ### parsed command line filenames and options...
  239.         $self->set_filenames_ref( $result_ref->{filenames} );
  240.  
  241.         # filenames_ref updated to: $self->get_filenames_ref();
  242.  
  243.         $self->set_is_confirm( ( $result_ref->{confirm} ) ? 1 : 0 );
  244.         ### is_confirm updated to: $self->get_is_confirm()
  245.  
  246.         $self->set_is_interactive( ( $result_ref->{interactive} ) ? 1 : 0 );
  247.         ### is_interactive updated to: $self->get_is_interactive()
  248.  
  249.         $self->set_is_verbose( ( $result_ref->{verbose} ) ? 1 : 0 );
  250.         ### is_verbose updated to: $self->get_is_verbose()
  251.  
  252.         $self->set_options_ref($result_ref);
  253.         ### options_ref updated to: $self->get_options_ref()
  254.  
  255.         return $result_ref;
  256.     }    # parse_cmdline
  257.  
  258.   # --------------------------------------------------------------------------
  259.   # Usage      : $controller->add_executable_links()
  260.   # Purpose    : Adds links from EXECUTABLES_DIR to executable files included
  261.   #              in provided filenames
  262.   # Returns    : nothing
  263.   # Parameters : $self - reference to this class instance
  264.   # Throws     : Any unanticipated error is returned to the caller
  265.     sub add_executable_links($self) {
  266.         ### add_executable_links() started...
  267.         ### assert: $self
  268.  
  269.         my @filenames = $self->executable_filenames();
  270.         if (@filenames) {
  271.             ### executables to link to: @filenames
  272.         }
  273.         else {
  274.             ### no executable files to add...
  275.             $self->print_warning_if_verbose("No executable files to add.\n");
  276.             return;
  277.         }
  278.  
  279.         ### ensure script is being executed as root...
  280.         if ($EFFECTIVE_USER_ID) {
  281.             $self->print_error(
  282.                       "\nCannot add symbolic links to executable "
  283.                     . "files if not the root user.\n" );
  284.             $self->confirm_quit(
  285.                 'Executable files cannot be added "
  286.                . "unless you are the root user.',
  287.                 $PERMISSION_ERR
  288.             );
  289.         }
  290.  
  291.         ### TODO add symbolic links to executable files...
  292.  
  293.         return;
  294.     }    # add_executable_links
  295.  
  296.     # ----------------------------------------------------------------------
  297.     # Usage      : $controller->add_systemd_links()
  298.     # Purpose    : Adds links from UNITS_DIR to systemd files included
  299.     #              in provided filenames
  300.     # Returns    : nothing
  301.     # Parameters : $self - reference to this class instance
  302.     # Throws     : Any unanticipated error is returned to the caller
  303.     sub add_systemd_links($self) {
  304.         ### add_systemd_links() started...
  305.         ### assert: $self
  306.  
  307.         my @filenames = $self->systemd_filenames();
  308.         if (@filenames) {
  309.             ### systemd files to link to: @filenames
  310.         }
  311.         else {
  312.             ### no systemd files to add...
  313.             $self->print_warning_if_verbose("No systemd files to add.\n");
  314.             return;
  315.         }
  316.  
  317.         ### ensure script is being executed as root...
  318.         if ($EFFECTIVE_USER_ID) {
  319.             $self->print_error( "\nCannot add symbolic links to systemd "
  320.                     . "files if not the root user.\n" );
  321.             $self->confirm_quit(
  322.                 'Systemd files cannot be linked to "
  323.                . "unless you are the root user.',
  324.                 $PERMISSION_ERR
  325.             );
  326.         }
  327.         ### TODO add symbolic links to systemd files...
  328.  
  329.         return;
  330.     }    # add_systemd_links
  331.  
  332.     # ----------------------------------------------------------------------
  333.     # Usage      : $controller->enable_executables()
  334.     # Purpose    : Enables executable files included in provided filenames
  335.     # Returns    : nothing
  336.     # Parameters : $self - reference to this class instance
  337.     # Throws     : Any unanticipated error is returned to the caller
  338.     # Comments   : Executables are not enabled, so this sub prints a
  339.     #              notification if given executables that this step is ignored
  340.     sub enable_executables($self) {
  341.         ### enable_executables() started...
  342.         ### assert: $self
  343.  
  344.         my @filenames = $self->executable_filenames();
  345.         if (@filenames) {
  346.             ### exectable files to enable: @filenames
  347.         }
  348.         else {
  349.             ### no executable files to enable...
  350.             $self->print_warning_if_verbose("No executable files to add.\n");
  351.             return;
  352.         }
  353.  
  354.         ### ignore enabling executables...
  355.         $self->print_info_if_verbose(
  356.             "Executable files are not enabled. Ignoring this step.\n");
  357.  
  358.         return;
  359.     }    # enable_executables
  360.  
  361.     # ----------------------------------------------------------------------
  362.     # Usage      : $controller->enable_systemd_services()
  363.     # Purpose    : Enables systemd services included in provided filenames
  364.     # Returns    : nothing
  365.     # Parameters : $self - reference to this class instance
  366.     # Throws     : Any unanticipated error is returned to the caller
  367.     sub enable_systemd_services($self) {
  368.         ### enable_systemd_services() started...
  369.         ### assert: $self
  370.  
  371.         my @filenames = $self->systemd_filenames();
  372.         if (@filenames) {
  373.             ### systemd files to enable: @filenames
  374.         }
  375.         else {
  376.             ### no systemd services to enable...
  377.             $self->print_warning_if_verbose(
  378.                 "No systemd services to enable.\n");
  379.             return;
  380.         }
  381.  
  382.         ### ensure script is being executed as root...
  383.         if ($EFFECTIVE_USER_ID) {
  384.             $self->print_error(
  385.                 "\nCannot enable systemd services if not the root user.\n");
  386.             $self->confirm_quit(
  387.                 'Cannot enable systemd services unless you are the root user.',
  388.                 $PERMISSION_ERR
  389.             );
  390.             return $PERMISSION_ERR;
  391.         }
  392.  
  393.         ### TODO enable systemd services...
  394.  
  395.         return;
  396.     }    # enable_systemd_services
  397.  
  398.     # ----------------------------------------------------------------------
  399.     # Usage      : $controller->enable_systemd_timers()
  400.     # Purpose    : Enables systemd timers included in provided filenames
  401.     # Returns    : nothing
  402.     # Parameters : $self - reference to this class instance
  403.     # Throws     : Any unanticipated error is returned to the caller
  404.     sub enable_systemd_timers($self) {
  405.         ### enable_systemd_timers() started...
  406.         ### assert: $self
  407.  
  408.         my @filenames = $self->timer_filenames();
  409.         if (@filenames) {
  410.             ### systemd timers to enable: @filenames
  411.         }
  412.         else {
  413.             ### no systemd timers to enable...
  414.             $self->print_warning_if_verbose("No systemd timers to enable.\n");
  415.             return;
  416.         }
  417.  
  418.         ### ensure script is being executed as root...
  419.         if ($EFFECTIVE_USER_ID) {
  420.             $self->print_error(
  421.                 "\nCannot enable systemd timers if not the root user.\n");
  422.             $self->confirm_quit(
  423.                 'Cannot enable systemd timers unless you are the root user.',
  424.                 $PERMISSION_ERR
  425.             );
  426.             return $PERMISSION_ERR;
  427.         }
  428.  
  429.         ### TODO enable systemd timers...
  430.  
  431.         return;
  432.     }    # enable_systemd_timers
  433.  
  434.     # ----------------------------------------------------------------------
  435.     # Usage      : $controller->list_executables()
  436.     # Purpose    : Lists executables included in provided filenames
  437.     # Returns    : nothing
  438.     # Parameters : $self - reference to this class instance
  439.     # Throws     : Any unanticipated error is returned to the caller
  440.     sub list_executables($self) {
  441.         ### list_executables() started...
  442.         ### assert: $self
  443.  
  444.         my @filenames = $self->executable_filenames();
  445.         if (@filenames) {
  446.             ### executable files to list: @filenames
  447.         }
  448.         else {
  449.             ### no executable files to list...
  450.             $self->print_warning_if_verbose("No executable files to list.\n");
  451.             return;
  452.         }
  453.  
  454.         ### TODO list executables...
  455.  
  456.         return;
  457.     }    # list_executables
  458.  
  459.     # ----------------------------------------------------------------------
  460.     # Usage      : $controller->list_services()
  461.     # Purpose    : Lists systemd services included in provided filenames
  462.     # Returns    : nothing
  463.     # Parameters : $self - reference to this class instance
  464.     # Throws     : Any unanticipated error is returned to the caller
  465.     sub list_services($self) {
  466.         ### list_services() started...
  467.         ### assert: $self
  468.  
  469.         my @filenames = $self->service_filenames();
  470.         if (@filenames) {
  471.             ### systemd services to list: @filenames
  472.         }
  473.         else {
  474.             ### no systemd services to list...
  475.             $self->print_warning_if_verbose("No systemd services to list.\n");
  476.             return;
  477.         }
  478.  
  479.         ### TODO list systemd services...
  480.  
  481.         return;
  482.     }    # list_services
  483.  
  484.     # ----------------------------------------------------------------------
  485.     # Usage      : $controller->list_timers()
  486.     # Purpose    : Lists systemd timers included in provided filenames
  487.     # Returns    : nothing
  488.     # Parameters : $self - reference to this class instance
  489.     # Throws     : Any unanticipated error is returned to the caller
  490.     sub list_timers($self) {
  491.         ### list_timers() started...
  492.         ### assert: $self
  493.  
  494.         my @filenames = $self->timer_filenames();
  495.         if (@filenames) {
  496.             ### systemd timers to list: @filenames
  497.         }
  498.         else {
  499.             ### no systemd timers to list...
  500.             $self->print_warning_if_verbose("No systemd timers to list.\n");
  501.             return;
  502.         }
  503.  
  504.         ### TODO list systemd timers...
  505.  
  506.         return;
  507.     }    # list_timers
  508.  
  509.     # ----------------------------------------------------------------------
  510.     # Usage      : $controller->remove_executable_links()
  511.     # Purpose    : Removes links to executables files
  512.     # Returns    : nothing
  513.     # Parameters : $self - reference to this class instance
  514.     # Throws     : Any unanticipated error is returned to the caller
  515.     sub remove_executable_links($self) {
  516.         ### remove_executable_links() started...
  517.         ### assert: $self
  518.  
  519.         my @filenames = $self->executable_filenames();
  520.         if (@filenames) {
  521.             ### executable files to remove: : @filenames
  522.         }
  523.         else {
  524.             ### no executable files to remove...
  525.             $self->print_warning_if_verbose(
  526.                 "No executable files to remove.\n");
  527.             return;
  528.         }
  529.  
  530.         ### ensure script is being executed as root...
  531.         if ($EFFECTIVE_USER_ID) {
  532.             $self->print_error( "\nCannot remove links to executable "
  533.                     . "files if not the root user.\n" );
  534.             $self->confirm_quit(
  535.                 'Cannot remove links to executable files "
  536.                . "unless you are the root user.',
  537.                 $PERMISSION_ERR
  538.             );
  539.             return $PERMISSION_ERR;
  540.         }
  541.  
  542.         ### TODO remove links to executable files...
  543.  
  544.         return;
  545.     }    # remove_executable_links
  546.  
  547.     # ----------------------------------------------------------------------
  548.     # Usage      : $controller->remove_systemd_links()
  549.     # Purpose    : Removes links to systemd files
  550.     # Returns    : nothing
  551.     # Parameters : $self - reference to this class instance
  552.     # Throws     : Any unanticipated error is returned to the caller
  553.     sub remove_systemd_links($self) {
  554.         ### remove_systemd_links() started...
  555.         ### assert: $self
  556.  
  557.         my @filenames = $self->systemd_filenames();
  558.         if (@filenames) {
  559.             ###  systemd files to remove: @filenames
  560.         }
  561.         else {
  562.             ### no systemd files to remove...
  563.             $self->print_warning_if_verbose("No systemd files to remove.\n");
  564.             return;
  565.         }
  566.  
  567.         ### ensure script is being executed as root...
  568.         if ($EFFECTIVE_USER_ID) {
  569.             $self->print_error(
  570.                 "\nCannot remove links to systemd files if not the root user.\n"
  571.             );
  572.             $self->confirm_quit(
  573.                 'Cannot remove links to systemd files "
  574.                . "unless you are the root user.',
  575.                 $PERMISSION_ERR
  576.             );
  577.             return $PERMISSION_ERR;
  578.         }
  579.  
  580.         ### TODO remove links to systemd files...
  581.  
  582.         return;
  583.     }    # remove_systemd_links
  584.  
  585.     # ------------------------------------------------------------------
  586.     # Usage      : @filenames = $controller->executable_filenames()
  587.     # Purpose    : Filters instance filenames for executable files
  588.     # Returns    : List of files that are executable
  589.     # Parameters : $self - reference to this class instance
  590.     # Throws     : Any unanticipated errors are returned to the caller
  591.     sub executable_filenames($self) {
  592.         ### executable_filenames() started...
  593.         ### assert: $self
  594.  
  595.         my @filenames = @{ $self->get_filenames_ref() };
  596.         ### command line filenames: @filenames
  597.  
  598.         my @executables = grep { !-d && -x } @filenames;
  599.         ### @executables
  600.  
  601.         return @executables;
  602.     }    # executable_filenames
  603.  
  604.     # ------------------------------------------------------------------
  605.     # Usage      : @filenames = $controller->systemd_filenames()
  606.     # Purpose    : Filters instance filenames for systemd unit files
  607.     # Returns    : List of files that are systemd units
  608.     # Parameters : $self - reference to this class instance
  609.     # Throws     : Any unanticipated errors are returned to the caller
  610.     sub systemd_filenames($self) {
  611.         ### systemd_filenames() started...
  612.         ### assert: $self
  613.  
  614.         my @filenames = @{ $self->get_filenames_ref() };
  615.         ### command line filenames: @filenames
  616.  
  617.         my @systemd_units = grep {
  618.             $ARG =~ qr{
  619.                 [.]             # literal dot
  620.                 (?:             # one of these extensions
  621.                       automount
  622.                     | device
  623.                     | mount
  624.                     | path
  625.                     | scope
  626.                     | service
  627.                     | slice
  628.                     | socket
  629.                     | swap
  630.                     | target
  631.                     | timer
  632.                 )
  633.                 \z              # end of string
  634.             }xms
  635.         } @filenames;
  636.         ### @systemd_units
  637.  
  638.         return @systemd_units;
  639.     }    # systemd_filenames
  640.  
  641.     # ------------------------------------------------------------------
  642.     # Usage      : @filenames = $controller->service_filenames()
  643.     # Purpose    : Filters instance filenames for systemd service files
  644.     # Returns    : List of files that are systemd services
  645.     # Parameters : $self - reference to this class instance
  646.     # Throws     : Any unanticipated errors are returned to the caller
  647.     sub service_filenames($self) {
  648.         ### service_filenames() started...
  649.         ### assert: $self
  650.  
  651.         my @filenames = @{ $self->get_filenames_ref() };
  652.         ### command line filenames: @filenames
  653.  
  654.         my @services = grep {
  655.             $ARG =~ qr{
  656.                 [.]     # literal dot
  657.                 service # service extension
  658.                 \z      # end of string
  659.             }xms
  660.         } @filenames;
  661.         ### @services
  662.  
  663.         return @services;
  664.     }    # service_filenames
  665.  
  666.     # ------------------------------------------------------------------
  667.     # Usage      : @filenames = $controller->timer_filenames()
  668.     # Purpose    : Filters instance filenames for systemd timer files
  669.     # Returns    : List of files that are systemd services
  670.     # Parameters : $self - reference to this class instance
  671.     # Throws     : Any unanticipated errors are returned to the caller
  672.     sub timer_filenames($self) {
  673.         ### remove_systemd_links() started...
  674.         ### assert: $self
  675.  
  676.         my @filenames = @{ $self->get_filenames_ref() };
  677.         ### command line filenames: @filenames
  678.  
  679.         my @timers = grep {
  680.             $ARG =~ qr{
  681.                 [.]     # literal dot
  682.                 timer   # timer extension
  683.                 \z      # end of string
  684.             }xms
  685.         } @filenames;
  686.         ### @timers
  687.  
  688.         return @timers;
  689.     }    # timer_filenames
  690.  
  691.     # ------------------------------------------------------------------
  692.     # Usage      : @filenames = $obj->confirm_quit($self, $msg, $code)
  693.     # Purpose    : Confirms that the program should exit.
  694.     # Returns    : nothing
  695.     # Parameters : $self - reference to this class instance
  696.     #              $msg - message shown to the user, followed by
  697.     #               " Quit? (y/n) "
  698.     #              $code - status/result code returned to the system
  699.     # Throws     : Any unanticipated errors are returned to the caller
  700.     # Comments   : If the program should exit then returns program status
  701.     #              $code to the operating system.
  702.     #              If not running interactively, or if the user enters an
  703.     #              empty string, then defaults to exiting the program
  704.     sub confirm_quit( $self, $msg, $code ) {
  705.         ### confirm_quit() started
  706.         ### assert: $self
  707.  
  708.         ### if not confirmation is not enabled then simply exit with $code
  709.         exit $code if ( !$self->is_confirm() );
  710.  
  711.         $msg = "\n$msg" . ' Quit? (y/n) ';
  712.         my $answer = '';
  713.         while ( $answer ne 'n' ) {
  714.             $answer = prompt( $msg, 'y' );
  715.             $answer = lc substr( $answer, 0, 1 );
  716.             exit $code if ( $answer eq 'y' );
  717.         }
  718.         return;
  719.     }    # confirm_quit
  720.  
  721.     # ------------------------------------------------------------------
  722.     # Usage      : $controller->print_info_if_verbose(@msg)
  723.     # Purpose    : Prints message and newline if the verbose option was
  724.     #              was given on the command line
  725.     # Returns    : nothing
  726.     # Parameters : $self - reference to this class instance
  727.     #              @msg - message printed to stderr
  728.     # Throws     : Any unanticipated error is returned to the caller
  729.     sub print_info_if_verbose( $self, @msg ) {
  730.         ### assert: $self
  731.  
  732.         $self->print_if_verbose( colored( @msg, $INFO_COLOR ) );
  733.         return;
  734.     }    # print_info_if_verbose
  735.  
  736.     # ------------------------------------------------------------------
  737.     # Usage      : $controller->print_status_if_verbose(@msg)
  738.     # Purpose    : Prints message and newline if the verbose option was
  739.     #              was given on the command line
  740.     # Returns    : nothing
  741.     # Parameters : $self - reference to this class instance
  742.     #              @msg - message printed to stderr
  743.     # Throws     : Any unanticipated error is returned to the caller
  744.     sub print_status_if_verbose( $self, @msg ) {
  745.         ### assert: $self
  746.  
  747.         $self->print_if_verbose( colored( @msg, $STATUS_COLOR ) );
  748.         return;
  749.     }    # print_status_if_verbose
  750.  
  751.     # ------------------------------------------------------------------
  752.     # Usage      : $controller->print_warning_if_verbose(@msg)
  753.     # Purpose    : Prints message and newline if the verbose option was
  754.     #              was given on the command line
  755.     # Returns    : nothing
  756.     # Parameters : $self - reference to this class instance
  757.     #              @msg - message printed to stderr
  758.     # Throws     : Any unanticipated error is returned to the caller
  759.     sub print_warning_if_verbose( $self, @msg ) {
  760.         ### assert: $self
  761.  
  762.         $self->print_if_verbose( $self->print_warning(@msg) );
  763.         return;
  764.     }    # print_warning_if_verbose
  765.  
  766.     # ------------------------------------------------------------------
  767.     # Usage      : $controller->print_if_verbose(@msg)
  768.     # Purpose    : Prints message and newline if the verbose option
  769.     #              was given on the command line
  770.     # Returns    : nothing
  771.     # Parameters : $self - reference to this class instance
  772.     #              @msg - message printed to stderr
  773.     # Throws     : Any unanticipated error is returned to the caller
  774.     sub print_if_verbose( $self, @msg ) {
  775.         ### assert: $self
  776.         ### print_if_verbose() started...
  777.         ### assert: $self
  778.  
  779.         if ( $self->get_is_verbose() ) {
  780.             print @msg;
  781.         }
  782.         return;
  783.     }    # print_if_verbose
  784.  
  785.     # ------------------------------------------------------------------
  786.     # Usage      : $controller->print_error(@msg)
  787.     # Purpose    : Prints message to standard error using error colors
  788.     # Returns    : nothing
  789.     # Parameters : $self - reference to this class instance
  790.     #              @msg - message printed to stderr
  791.     # Throws     : Any unanticipated error is returned to the caller
  792.     sub print_error( $self, @msg ) {
  793.         ### assert: $self
  794.  
  795.         print_stderr( colored( @msg, $ERROR_COLOR ) );
  796.         return;
  797.     }    # print_error
  798.  
  799.     # ------------------------------------------------------------------
  800.     # Usage      : $controller->print_warning(@msg)
  801.     # Purpose    : Prints message using warning colors
  802.     # Returns    : nothing
  803.     # Parameters : $self - reference to this class instance
  804.     #              @msg - message printed to stderr
  805.     # Throws     : Any unanticipated error is returned to the caller
  806.     sub print_warning( $self, @msg ) {
  807.         ### assert: $self
  808.  
  809.         print colored( @msg, $WARNING_COLOR );
  810.         return;
  811.     }    # print_warning
  812.  
  813.     # ------------------------------------------------------------------
  814.     # Usage      : $controller->error_exit($code, @msg)
  815.     # Purpose    : Prints message to standard error and then exits the
  816.     #              program with code
  817.     # Returns    : Does not return
  818.     # Parameters : $self - reference to this class instance
  819.     #              $code - exit status code (default: GENERAL_ERR)
  820.     #            : @msg - message printed to stderr
  821.     #                   (default: 'Something went wrong.')
  822.     # Throws     : Any unanticipated error is returned to the caller
  823.     sub error_exit( $self, $code, @msg ) {
  824.         ### error_exit() started...
  825.         ### assert: $self
  826.         $code = $GENERAL_ERR if ( !defined $code );
  827.         ### $code
  828.         @msg = ('Something went wrong.') if ( !keys @msg );
  829.         ### @msg
  830.  
  831.         $self->print_error(@msg);
  832.         exit $code;
  833.     }    # error_exit
  834.  
  835.     # ------------------------------------------------------------------
  836.     # Usage      : print_stderr(@msg)
  837.     # Purpose    : Prints message to error output
  838.     # Returns    : nothing
  839.     # Parameters : @msg - message printed to stderr
  840.     # Throws     : Any unanticipated error is returned to the caller
  841.     sub print_stderr(@msg) {
  842.         ### print_stderr() started...
  843.         @msg = ('Something went wrong.') if ( !keys @msg );
  844.         ### @msg
  845.  
  846.         return print STDERR join( $LIST_SEPARATOR, @msg );
  847.     }    # print_stderr
  848.  
  849. }    # SystemdController
  850.  
  851. # my $cron_list = capturex( 'crontab', '-l' );
  852. # print {$out} $cron_list;
  853.  
  854. __END__
  855.  
  856. =pod
  857.  
  858. =head1 NAME
  859.  
  860. systemd-control.pl - maintains personal systemd services, timers,
  861. and executables.
  862.  
  863. =head1 VERSION
  864.  
  865. This document describes B<systemd-control.pl> version B<0.1.0>
  866.  
  867. =head1 USAGE
  868.  
  869. B<systemd-control.pl> [I<option>]... [I<filename>]...
  870.  
  871. B<systemd-control.pl> --help
  872.  
  873. B<systemd-control.pl> --usage
  874.  
  875. B<systemd-control.pl> --man
  876.  
  877. B<systemd-control.pl> --version
  878.  
  879. B<systemd-control.pl> --add --enable -v *.timer
  880.  
  881. B<systemd-control.pl> --remove --confirm *.service myexe
  882.  
  883. B<systemd-control.pl> -x --list-services --list-timers
  884.  
  885. =head1 DESCRIPTION
  886.  
  887. B<systemd-control.pl> maintains personal L<systemd(1)> services, timers,
  888. and executables.
  889.  
  890. This program is used to integrate local systemd units and executables into the
  891. Unix/Linux environment, and to maintain the same.
  892.  
  893. =head1 REQUIRED ARGUMENTS
  894.  
  895. There are no required arguments.
  896.  
  897. =head1 OPTIONS
  898.  
  899. =over
  900.  
  901. =item <filename>...
  902.  
  903. Names of the files on which to perform the requested options (e.g., C<--add>).
  904.  
  905. =item -a | --add
  906.  
  907. Creates symbolic links to the given local files to integrate them into the
  908. Unix/Linux system.
  909.  
  910. Symbolic links to systemd units are written to the C</etc/systemd/system>
  911. directory, and symbolic links to executables are written to the
  912. C</usr/local/bin> directory.
  913.  
  914. =item -r | --remove
  915.  
  916. Removes symbolic links to the given local files.
  917.  
  918. Symbolic links to systemd units are deleted from anywhere they occur under the
  919. C</etc> or C</usr> directories.
  920.  
  921. Symbolic links to executables are deleted from the C</usr/local/bin> directory.
  922.  
  923. =item -e | --enable
  924.  
  925. Enables the given systemd services and timers.
  926.  
  927. Timers are automatically started when enabled.
  928.  
  929. Executables do not need to be enabled, and are ignored.
  930.  
  931. =item -c | --confirm
  932.  
  933. Prints whether or not the given systemd services, systemd timers, and
  934. executables have been added (i.e., linked) to.
  935.  
  936. Prints the L<systemctl(1)> status of services/timers.
  937.  
  938. =item -s | --[list-]services
  939.  
  940. Lists the local services that have been added to the system.
  941.  
  942. =item -t | --[list-]timers
  943.  
  944. Lists the local timers that have been added to the system.
  945.  
  946. =item -x | --[list-]executables
  947.  
  948. Lists the local executables that have been added to the system.
  949.  
  950. =item -i | --interactive
  951.  
  952. Asks whether or not to create missing directories, create symbolic links, or
  953. remove obsolete symbolic links.
  954.  
  955. =item -v | --verbose
  956.  
  957. Prints messages of what is being done.
  958.  
  959. =item -h | -? | --help
  960.  
  961. Prints help for this program and exits.
  962.  
  963. =item --usage
  964.  
  965. Prints the usage summary for this program and exits.
  966.  
  967. =item --man
  968.  
  969. Prints the POD manual for this program and exits.
  970.  
  971. =item --version
  972.  
  973. Prints the program version and exits.
  974.  
  975. =back
  976.  
  977. =head1 CONFIGURATION
  978.  
  979. This program is configured entirely by command line options and arguments.
  980.  
  981. =head1 DEPENDENCIES
  982.  
  983. B<Perl> version B<5.40.0> or later.
  984.  
  985. =head2 In The Standard Distribution
  986.  
  987. =over
  988.  
  989. =item B<Carp>
  990.  
  991. An alternative warn and die.
  992.  
  993. =item B<Data::Dumper>
  994.  
  995. Stringifies Perl data structures.
  996.  
  997. =item B<English>
  998.  
  999. Nice English (or awk) names for ugly punctuation variables.
  1000.  
  1001. =item B<File::Find>
  1002.  
  1003. Traverses a directory tree.
  1004.  
  1005. =item B<Getopt::Long>
  1006.  
  1007. Extended processing of command line options.
  1008.  
  1009. =item B<Pod::Usage>
  1010.  
  1011. Extracts POD documentation and shows usage information. This is used for the
  1012. I<help>, I<usage>, and I<man> command line options.
  1013.  
  1014. =item B<autodie>
  1015.  
  1016. Replaces functions with ones that succeed or die with lexical scope.
  1017.  
  1018. =item B<strict>
  1019.  
  1020. Restricts unsafe constructs.
  1021.  
  1022. =item B<version>
  1023.  
  1024. Declares version numbers.
  1025.  
  1026. =item B<warnings>
  1027.  
  1028. Controls optional warnings.
  1029.  
  1030. =back
  1031.  
  1032. =head2 Must Be Installed Separately
  1033.  
  1034. =over
  1035.  
  1036. =item B<Class::Std>
  1037.  
  1038. Supports creating standard inside-out classes
  1039.  
  1040. =item B<IPC::System::Simple>
  1041.  
  1042. Runs commands simply, with detailed diagnostics.
  1043.  
  1044. =item B<Readonly>
  1045.  
  1046. Facilitates creating read-only scalars, arrays, and hashes.
  1047.  
  1048. =item B<Smart::Comments>
  1049.  
  1050. Provides an easy way to insert debugging and tracking code into the program.
  1051.  
  1052. =item B<Term::ANSIColor>
  1053.  
  1054. Colors screen output using ANSI escape sequences.
  1055.  
  1056. =item B<strictures>
  1057.  
  1058. Turns on strict and makes most warnings fatal.
  1059.  
  1060. =back
  1061.  
  1062. =head1 EXIT STATUS
  1063.  
  1064. On successful completion this program returns status code B<0>, otherwise
  1065. please see the B<Diagnostics> section for an explanation of the error message
  1066. and the returned status code.
  1067.  
  1068. =head1 DIAGNOSTICS
  1069.  
  1070. TODO update Diagnostics section
  1071.  
  1072. =head2 Status Code 0
  1073.  
  1074. If the I<help>, I<usage>, I<man>, or I<version> option is given on the command
  1075. line, then its contents will be written and the program will exit.
  1076.  
  1077. =head2 Status Code 2
  1078.  
  1079. =over
  1080.  
  1081. =item B<Undefined subroutine <subroutine name> called at ...>
  1082.  
  1083. This will be printed if L<cron(8)> is not available on your system. Please
  1084. ensure your system has L<cron(8)>.
  1085.  
  1086. =item B<Can't locate <package> in @INC ...>
  1087.  
  1088. This will be printed if any of the required Perl package dependencies is not
  1089. installed. Please install any necessary packages to proceed.
  1090.  
  1091. =item B<### ...>
  1092.  
  1093. This means that the C<Smart::Comments> package is enabled.  If you want to turn
  1094. these off then locate B<systemd-control.pl> (this file) and
  1095. disable the line "use Smart::Comments ...", either by starting the line with a
  1096. "#", or by deleting the line.
  1097.  
  1098. If the C<Smart::Comments> package is enabled then please report it as a bug.
  1099.  
  1100. =back
  1101.  
  1102. =head2 Status Code 77
  1103.  
  1104. This means that a permission error has occurred. This program needs to be run
  1105. as/by the root user.
  1106.  
  1107. =over
  1108.  
  1109. =item B<Various Error Messages>
  1110.  
  1111. This means that an error occurred while trying to execute a system command.
  1112.  
  1113. Ensure that the named command is available and can be found by
  1114. B<systemd-control.pl>. You can try to run the system command from
  1115. the directory B<systemd-control.pl> is in to ensure that the
  1116. system command can be executed by B<systemd-control.pl>.
  1117.  
  1118. =back
  1119.  
  1120. =head1 INCOMPATIBILITIES
  1121.  
  1122. This program has no known incompatibilities.
  1123.  
  1124. Please report incompatibilities to Justin Hanekom ([email protected]).
  1125.  
  1126. =head1 BUGS AND LIMITATIONS
  1127.  
  1128. This program has no known bugs or limitations.
  1129.  
  1130. Please report problems to Justin Hanekom ([email protected]).
  1131.  
  1132. =head1 AUTHOR
  1133.  
  1134. This program was initially written by Justin Hanekom ([email protected]).
  1135.  
  1136. =head1 LICENSE AND COPYRIGHT
  1137.  
  1138. This is free and unencumbered software released into the public domain.
  1139.  
  1140. Anyone is free to copy, modify, publish, use, compile, sell, or
  1141. distribute this software, either in source code form or as a compiled
  1142. binary, for any purpose, commercial or non-commercial, and by any
  1143. means.
  1144.  
  1145. In jurisdictions that recognize copyright laws, the author or authors
  1146. of this software dedicate any and all copyright interest in the
  1147. software to the public domain. We make this dedication for the benefit
  1148. of the public at large and to the detriment of our heirs and
  1149. successors. We intend this dedication to be an overt act of
  1150. relinquishment in perpetuity of all present and future rights to this
  1151. software under copyright law.
  1152.  
  1153. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  1154. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  1155. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  1156. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  1157. OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  1158. ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  1159. OTHER DEALINGS IN THE SOFTWARE.
  1160.  
  1161. For more information, please refer to <https://unlicense.org/>
  1162.  
  1163. =cut
  1164.  
Advertisement
Add Comment
Please, Sign In to add comment