{"id":20315239,"url":"https://github.com/sysread/proc-tored","last_synced_at":"2025-09-19T07:26:26.352Z","repository":{"id":56836001,"uuid":"80773069","full_name":"sysread/Proc-tored","owner":"sysread","description":"Perl package for managing a service with a pid file","archived":false,"fork":false,"pushed_at":"2017-10-27T18:25:34.000Z","size":128,"stargazers_count":0,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-01T02:01:37.943Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sysread.png","metadata":{"files":{"readme":"README.pod","changelog":"Changes","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-02T22:03:41.000Z","updated_at":"2017-02-02T22:04:47.000Z","dependencies_parsed_at":"2022-09-07T11:40:57.317Z","dependency_job_id":null,"html_url":"https://github.com/sysread/Proc-tored","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/sysread/Proc-tored","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FProc-tored","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FProc-tored/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FProc-tored/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FProc-tored/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sysread","download_url":"https://codeload.github.com/sysread/Proc-tored/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FProc-tored/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275895516,"owners_count":25547970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-19T02:00:09.700Z","response_time":108,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T18:18:25.712Z","updated_at":"2025-09-19T07:26:26.304Z","avatar_url":"https://github.com/sysread.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"=pod\n\n=encoding UTF-8\n\n=head1 NAME\n\nProc::tored - Service management using a pid file and touch files\n\n=head1 VERSION\n\nversion 0.20\n\n=head1 SYNOPSIS\n\n  use Proc::tored;\n  use Getopt::Long;\n\n  my %opt = (\n    pause  =\u003e 0,\n    resume =\u003e 0,\n    stop   =\u003e 0,\n    zap    =\u003e 0,\n    start  =\u003e 0,\n    run    =\u003e 0,\n  );\n\n  GetOptions(\n    'pause'  =\u003e \\$opt{pause},\n    'resume' =\u003e \\$opt{resume},\n    'stop'   =\u003e \\$opt{stop},\n    'zap'    =\u003e \\$opt{zap},\n    'start'  =\u003e \\$opt{start},\n    'run'    =\u003e \\$opt{run},\n  );\n\n  my $service = service 'stuff-doer', in '/var/run';\n  my $pid = running $service;\n\n  print \"Running service found with pid $pid\\n\"\n    if $pid;\n\n  if ($opt{pause}) {\n    # Set the paused flag, causing a running service to block until unset\n    pause $service;\n  }\n  elsif ($opt{resume}) {\n    # Unset the paused flag, unblocking any running service\n    resume $service;\n  }\n  elsif ($opt{stop}) {\n    # Set the stopped state, preventing new processes from starting the service\n    # and causing running processes to self-terminate\n    stop $service;\n  }\n  elsif ($opt{zap}) {\n    # Terminate a running process, timing out after 15s\n    zap $service, 15\n      or die \"stuff_doer $pid is being stubborn\";\n  }\n  elsif ($opt{start}) {\n    # Allow the service to start running again\n    start $service;\n  }\n\n  if ($opt{run}) {\n    # Run service (if not stopped)\n    run { do_stuff() } $service;\n  }\n\n=head1 DESCRIPTION\n\nA C\u003cProc::tored\u003e service is voluntarily managed by a pid file and touch files.\n\nC\u003cProc::tored\u003e services are specified with a name and a path. Any services\ncreated using the same name and path are considered the same service and will\nbe aware of other processes via their L\u003c/PID FILE\u003e and respect service control\nL\u003c/FLAGS\u003e.\n\n=head1 EXPORTED SUBROUTINES\n\nAll routines are exported by default.\n\n=head2 service\n\n=head2 in\n\n=head2 trap\n\nA proctored service is defined using the C\u003cservice\u003e function. The name given to\nthe service is used in the naming of various files used to control the service\n(e.g., pid file and touch files). The C\u003cin\u003e function is used to specify the\nlocal directory where these files will be created and looked for. Signals may\nbe trapped using C\u003ctrap\u003e on non-C\u003cMSWin32\u003e systems.\n\n  my $service = service 'name-of-service', in '/var/run', trap ['TERM', 'INT'];\n\n=head2 pid\n\nReads and returns the contents of the pid file. Does not check to determine\nwhether the pid is valid. Returns 0 if the pid file is not found or is empty.\n\n  printf \"service may be running under pid %d\", pid $service;\n\n=head2 running\n\nReads and returns the contents of the pid file after checking that the process\nidentified still exists. Essentially the same as C\u003ckill(0, pid $service)\u003e.\nReturns 0 if the pid is not found or cannot be signalled.\n\n  if (my $pid = running $service) {\n    warn \"service is already running under pid $pid\";\n  }\n\n=head2 run\n\nBegins the service in the current process. The service, specified as a code\nblock, will be called until it returns false or the L\u003c/stopped\u003e flag is set.\n\nIf the L\u003c/paused\u003e flag is set, the loop will continue to run without executing\nthe code block until it has been L\u003c/resume\u003ed. If the L\u003c/paused\u003e flag is set at\nthe time C\u003crun\u003e is called, the loop will start but will not begin executing the\ncode block until the flag is cleared.\n\nIf the L\u003c/stopped\u003e flag is set, the loop will terminate at the completion of\nthe current iteration. If the L\u003c/stopped\u003e flag is set at the time C\u003crun\u003e is\ncalled, C\u003crun\u003e will return false immediately. The behavior under L\u003c/stopped\u003e\ntakes priority over that of L\u003c/paused\u003e.\n\n  my $started = time;\n  my $max_run_time = 300;\n\n  run {\n    if (time - $started \u003e $max_run_time) {\n      warn \"Max run time ($max_run_time seconds) exceeded\\n\";\n      warn \"  -shutting down\\n\";\n      return 0;\n    }\n    else {\n      do_some_work();\n    }\n\n    return 1;\n  } $service;\n\n=head2 zap\n\nSets the \"stopped\" flag (see L\u003c/stop\u003e), then blocks until a running service\nexits. Returns immediately (after setting the \"stopped\" flag) if the\nL\u003c/running\u003e service is the current process.\n\n  sub stop_service {\n    if (my $pid = running $service) {\n      print \"Attempting to stop running service running under process $pid\\n\";\n\n      if (zap $pid, 30) {\n        print \"  -Service shut down\\n\";\n        return 1;\n      }\n      else {\n        print \"  -Timed out before service shut down\\n\";\n        return 0;\n      }\n    }\n  }\n\n=head2 stop\n\n=head2 start\n\n=head2 stopped\n\nControls and inspects the \"stopped\" flag for the service.\n\n  # Stop a running service\n  if (!stopped $service \u0026\u0026 running $service) {\n    stop $service;\n  }\n\n  do_work_while_stopped();\n\n  # Allow service to start\n  # Note that this does not launch the service process. It simply clears the\n  # \"stopped\" flag that would have prevented it from running again.\n  start $service;\n\n=head2 pause\n\n=head2 resume\n\n=head2 paused\n\nControls and inspects the \"paused\" flag for the service. In general, this\nshould never be done inside the L\u003c/run\u003e loop (see the warning in L\u003c/Pause and\nresume\u003e).\n\n  # Pause a running service\n  # Note that the running service will not exit. Instead, it will stop\n  # executing its main loop until the \"paused\" flag is cleared.\n  if (!paused $service \u0026\u0026 running $service) {\n    pause $service;\n  }\n\n  do_work_while_paused();\n\n  # Allow service to resume execution\n  resume $service;\n\n=head1 PID FILE\n\nA pid file is used to identify a running service. While the service is running,\nbarring any outside interference, the pid will contain the pid of the running\nprocess and a newline. After the service process stops, the pid file will be\ntruncated. The file will be located in the directory specified by L\u003c/in\u003e. Its\nname is the concatenation of the service name and \".pid\".\n\n=head1 FLAGS\n\nService control flags are persistent until unset. Their status is determined by\nthe existence of a touch file.\n\n=head2 stopped\n\nA touch file indicating that a running service should self-terminate and that\nnew processes should not start is created with L\u003c/stop\u003e and removed with\nL\u003c/start\u003e. It is located in the directory specified by L\u003c/in\u003e. Its name is the\nconcatenation of the service name and \".stopped\".\n\n=head2 paused\n\nA touch file indicating that a running service should temporarily stop\nexecuting and that new processes should start but not yet execute any service\ncode is created with L\u003c/pause\u003e and removed with L\u003c/resume\u003e. It is located in\nthe directory specified by L\u003c/in\u003e. Its name is the concatenation of the service\nname and \".paused\".\n\n=head1 BUGS AND LIMITATIONS\n\n=head2 Pause and resume\n\nWhen a service is L\u003c/paused\u003e, the code block passed to L\u003c/run\u003e is no longer\nexecuted until I\u003csomething\u003e calls L\u003c/resume\u003e. This can lead to deadlock if\nthere is no external actor willing to L\u003c/resume\u003e the service.\n\nFor example, this service will never resume:\n\n  run {\n    my $empty = out_of_tasks();\n\n    if ($empty) {\n      pause $service;\n    }\n    elsif (paused $service \u0026\u0026 !$empty) {\n      # This line is never reached because this code block is no longer\n      # executed after being paused above.\n      resume $service;\n    }\n\n    do_next_task();\n    return 1;\n  } $service;\n\nIn most cases, pausing and resuming a service should be handled from outside of\nL\u003c/run\u003e.\n\n=head1 AUTHOR\n\nJeff Ober \u003csysread@fastmail.fm\u003e\n\n=head1 COPYRIGHT AND LICENSE\n\nThis software is copyright (c) 2017 by Jeff Ober.\n\nThis is free software; you can redistribute it and/or modify it under\nthe same terms as the Perl 5 programming language system itself.\n\n=cut\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsysread%2Fproc-tored","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsysread%2Fproc-tored","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsysread%2Fproc-tored/lists"}