From f51179ff974330fa72e4e9217c4075acd51956ca Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Tue, 31 Oct 2023 09:54:54 -0400 Subject: [PATCH] Add papplSystemGet/SetIdleShutdown APIs to support idle shutdown of the system (Issue #304) --- CHANGES.md | 2 ++ doc/pappl.html | 31 ++++++++++++++++++ man/pappl-client.3 | 2 +- man/pappl-device.3 | 2 +- man/pappl-job.3 | 2 +- man/pappl-log.3 | 2 +- man/pappl-mainloop.3 | 2 +- man/pappl-printer.3 | 2 +- man/pappl-resource.3 | 2 +- man/pappl-system.3 | 26 ++++++++++++++- pappl/libpappl2.def | 2 ++ pappl/system-accessors.c | 48 +++++++++++++++++++++++++++ pappl/system-private.h | 1 + pappl/system.c | 41 +++++++++++++++++------ pappl/system.h | 2 ++ testsuite/testpappl.c | 70 ++++++++++++++++++++++++++++++++++++++++ 16 files changed, 219 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1370c248..c6d4cc3e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,8 @@ Changes in v2.0b1 - Now require libcups v3 or higher. - Increased `PAPPL_MAX_TYPE` to 128 (Issue #268) +- Added `papplSystemGet/SetIdleShutdown` APIs to get/set the idle shutdown + time in seconds (Issue #304) - Added "smi55357-device-uri" and "smi55357-driver" Printer Status attributes to Get-Printer-Attributes responses. - Updated `papplDeviceOpen` and `pappl_devopen_cb_t` to accept a `pappl_job_t` diff --git a/doc/pappl.html b/doc/pappl.html index 7d3ad7e5..605a9d82 100644 --- a/doc/pappl.html +++ b/doc/pappl.html @@ -487,6 +487,7 @@

Contents

  • papplSystemGetGeoLocation
  • papplSystemGetHostName
  • papplSystemGetHostPort
  • +
  • papplSystemGetIdleShutdown
  • papplSystemGetLocation
  • papplSystemGetLogLevel
  • papplSystemGetMaxClients
  • @@ -525,6 +526,7 @@

    Contents

  • papplSystemSetFooterHTML
  • papplSystemSetGeoLocation
  • papplSystemSetHostName
  • +
  • papplSystemSetIdleShutdown
  • papplSystemSetLocation
  • papplSystemSetLogLevel
  • papplSystemSetMIMECallback
  • @@ -5454,6 +5456,20 @@

    Return Value

    Discussion

    This function returns the port number that is used for network connections to the system.

    +

    papplSystemGetIdleShutdown

    +

    Get the system idle shutdown value.

    +

    +int papplSystemGetIdleShutdown(pappl_system_t *system);

    +

    Parameters

    + + + +
    systemSystem
    +

    Return Value

    +

    Idle shutdown value in seconds

    +

    Discussion

    +

    This function gets the system idle shutdown value in seconds. A value of 0 +means that idle shutdown is disabled.

    papplSystemGetLocation

    Get the system location string, if any.

    @@ -6087,6 +6103,21 @@

    Parameters

    Discussion

    This function sets the system hostname. If NULL, the default hostname is used.

    +

    papplSystemSetIdleShutdown

    +

    Set the idle shutdown value.

    +

    +void papplSystemSetIdleShutdown(pappl_system_t *system, int seconds);

    +

    Parameters

    + + + + + +
    systemSystem
    secondsSeconds
    +

    Discussion

    +

    This function sets the idle shutdown value in seconds. If the system does +not receive any requests or process any jobs within the specified timeframe, +it will automatically shutdown. A value of 0 disables auto-shutdown.

    papplSystemSetLocation

    Set the system location string, if any.

    diff --git a/man/pappl-client.3 b/man/pappl-client.3 index a3d888d9..b6bca003 100644 --- a/man/pappl-client.3 +++ b/man/pappl-client.3 @@ -1,4 +1,4 @@ -.TH pappl-client 3 "pappl client functions" "2023-10-28" "pappl client functions" +.TH pappl-client 3 "pappl client functions" "2023-10-31" "pappl client functions" .SH NAME pappl-client \- pappl client functions .SH LIBRARY diff --git a/man/pappl-device.3 b/man/pappl-device.3 index 5b3c9e0f..697fda20 100644 --- a/man/pappl-device.3 +++ b/man/pappl-device.3 @@ -1,4 +1,4 @@ -.TH pappl-device 3 "pappl device functions" "2023-10-28" "pappl device functions" +.TH pappl-device 3 "pappl device functions" "2023-10-31" "pappl device functions" .SH NAME pappl-device \- pappl device functions .SH LIBRARY diff --git a/man/pappl-job.3 b/man/pappl-job.3 index c9ce5894..7a41d995 100644 --- a/man/pappl-job.3 +++ b/man/pappl-job.3 @@ -1,4 +1,4 @@ -.TH pappl-job 3 "pappl job functions" "2023-10-28" "pappl job functions" +.TH pappl-job 3 "pappl job functions" "2023-10-31" "pappl job functions" .SH NAME pappl-job \- pappl job functions .SH LIBRARY diff --git a/man/pappl-log.3 b/man/pappl-log.3 index 4c767203..677ba20f 100644 --- a/man/pappl-log.3 +++ b/man/pappl-log.3 @@ -1,4 +1,4 @@ -.TH pappl-log 3 "pappl logging functions" "2023-10-28" "pappl logging functions" +.TH pappl-log 3 "pappl logging functions" "2023-10-31" "pappl logging functions" .SH NAME pappl-log \- pappl logging functions .SH LIBRARY diff --git a/man/pappl-mainloop.3 b/man/pappl-mainloop.3 index 120d7a53..411fe51e 100644 --- a/man/pappl-mainloop.3 +++ b/man/pappl-mainloop.3 @@ -1,4 +1,4 @@ -.TH pappl-mainloop 3 "pappl main loop functions" "2023-10-28" "pappl main loop functions" +.TH pappl-mainloop 3 "pappl main loop functions" "2023-10-31" "pappl main loop functions" .SH NAME pappl-mainloop \- pappl main loop functions .SH LIBRARY diff --git a/man/pappl-printer.3 b/man/pappl-printer.3 index 671e31a0..bb0a55cd 100644 --- a/man/pappl-printer.3 +++ b/man/pappl-printer.3 @@ -1,4 +1,4 @@ -.TH pappl-printer 3 "pappl printer functions" "2023-10-28" "pappl printer functions" +.TH pappl-printer 3 "pappl printer functions" "2023-10-31" "pappl printer functions" .SH NAME pappl-printer \- pappl printer functions .SH LIBRARY diff --git a/man/pappl-resource.3 b/man/pappl-resource.3 index f4be3f1a..abc3c004 100644 --- a/man/pappl-resource.3 +++ b/man/pappl-resource.3 @@ -1,4 +1,4 @@ -.TH pappl-resource 3 "pappl resource functions" "2023-10-28" "pappl resource functions" +.TH pappl-resource 3 "pappl resource functions" "2023-10-31" "pappl resource functions" .SH NAME pappl-resource \- pappl resource functions .SH LIBRARY diff --git a/man/pappl-system.3 b/man/pappl-system.3 index ce14a23a..ee7af641 100644 --- a/man/pappl-system.3 +++ b/man/pappl-system.3 @@ -1,4 +1,4 @@ -.TH pappl-system 3 "pappl system functions" "2023-10-28" "pappl system functions" +.TH pappl-system 3 "pappl system functions" "2023-10-31" "pappl system functions" .SH NAME pappl-system \- pappl system functions .SH LIBRARY @@ -714,6 +714,17 @@ int papplSystemGetHostPort ( .PP This function returns the port number that is used for network connections to the system. +.SS papplSystemGetIdleShutdown +Get the system idle shutdown value. +.PP +.nf +int papplSystemGetIdleShutdown ( + pappl_system_t *system +); +.fi +.PP +This function gets the system idle shutdown value in seconds. A value of \fB0\fR +means that idle shutdown is disabled. .SS papplSystemGetLocation Get the system location string, if any. .PP @@ -1204,6 +1215,19 @@ void papplSystemSetHostName ( .PP This function sets the system hostname. If \fBNULL\fR, the default hostname is used. +.SS papplSystemSetIdleShutdown +Set the idle shutdown value. +.PP +.nf +void papplSystemSetIdleShutdown ( + pappl_system_t *system, + int seconds +); +.fi +.PP +This function sets the idle shutdown value in seconds. If the system does +not receive any requests or process any jobs within the specified timeframe, +it will automatically shutdown. A value of \fB0\fR disables auto-shutdown. .SS papplSystemSetLocation Set the system location string, if any. .PP diff --git a/pappl/libpappl2.def b/pappl/libpappl2.def index afb3f677..941657f1 100644 --- a/pappl/libpappl2.def +++ b/pappl/libpappl2.def @@ -210,6 +210,7 @@ papplSystemGetGeoLocation papplSystemGetHostName papplSystemGetHostPort papplSystemGetHostname +papplSystemGetIdleShutdown papplSystemGetLocation papplSystemGetLogLevel papplSystemGetMaxClients @@ -250,6 +251,7 @@ papplSystemSetFooterHTML papplSystemSetGeoLocation papplSystemSetHostName papplSystemSetHostname +papplSystemSetIdleShutdown papplSystemSetLocation papplSystemSetLogLevel papplSystemSetMIMECallback diff --git a/pappl/system-accessors.c b/pappl/system-accessors.c index c349ea4c..1a6de334 100644 --- a/pappl/system-accessors.c +++ b/pappl/system-accessors.c @@ -723,6 +723,32 @@ papplSystemGetHostPort( } +// +// 'papplSystemGetIdleShutdown()' - Get the system idle shutdown value. +// +// This function gets the system idle shutdown value in seconds. A value of `0` +// means that idle shutdown is disabled. +// + +int // O - Idle shutdown value in seconds +papplSystemGetIdleShutdown( + pappl_system_t *system) // I - System +{ + int ret = 0; // Return value + + + if (system) + { + _papplRWLockRead(system); + ret = system->idle_shutdown; + _papplRWUnlock(system); + } + + return (ret); + +} + + // // 'papplSystemGetLocation()' - Get the system location string, if any. // @@ -1823,6 +1849,28 @@ papplSystemSetHostName( } +// +// 'papplSystemSetIdleShutdown()' - Set the idle shutdown value. +// +// This function sets the idle shutdown value in seconds. If the system does +// not receive any requests or process any jobs within the specified timeframe, +// it will automatically shutdown. A value of `0` disables auto-shutdown. +// + +void +papplSystemSetIdleShutdown( + pappl_system_t *system, // I - System + int seconds) // I - Seconds +{ + if (system && seconds >= 0) + { + _papplRWLockWrite(system); + system->idle_shutdown = seconds; + _papplRWUnlock(system); + } +} + + // // 'papplSystemSetLocation()' - Set the system location string, if any. // diff --git a/pappl/system-private.h b/pappl/system-private.h index 6d654f67..4dc3ce92 100644 --- a/pappl/system-private.h +++ b/pappl/system-private.h @@ -79,6 +79,7 @@ struct _pappl_system_s // System data size_t logmaxsize; // Maximum log file size or `0` for none char *subtypes; // DNS-SD sub-types, if any bool tls_only; // Only support TLS? + int idle_shutdown; // Idle shutdown limit in seconds char *auth_service; // PAM authorization service, if any char *admin_group; // PAM administrative group, if any gid_t admin_gid; // PAM administrative group ID diff --git a/pappl/system.c b/pappl/system.c index c10576e0..71962955 100644 --- a/pappl/system.c +++ b/pappl/system.c @@ -402,7 +402,9 @@ papplSystemRun(pappl_system_t *system) // I - System pappl_printer_t *printer; // Current printer struct timeval curtime; // Current time time_t next, // Next time for scheduling... + idletime, // Last time we saw activity subtime = 0; // Subscription checking time + size_t jcount = 0; // Number of active jobs _pappl_timer_t *timer; // Current timer bool save_changes; // Save changes? @@ -566,6 +568,8 @@ papplSystemRun(pappl_system_t *system) // I - System } // Loop until we are shutdown or have a hard error... + time(&idletime); + for (;;) { if (restart_logging) @@ -580,6 +584,8 @@ papplSystemRun(pappl_system_t *system) // I - System if (system->shutdown_time || sigterm_time) next = curtime.tv_sec + 1; + else if (system->idle_shutdown > 0) + next = idletime + system->idle_shutdown; else next = curtime.tv_sec + 30; @@ -608,6 +614,8 @@ papplSystemRun(pappl_system_t *system) // I - System if (pcount > 0) { // Accept client connections as needed... + time(&idletime); + for (i = 0; i < (size_t)system->num_listeners; i ++) { if (system->listeners[i].revents & POLLIN) @@ -695,26 +703,39 @@ papplSystemRun(pappl_system_t *system) // I - System } _papplRWLockRead(system); - if (system->shutdown_time || sigterm_time) + if (system->idle_shutdown && (time(NULL) - idletime) >= system->idle_shutdown) { - // Shutdown requested, see if we can do so safely... - size_t jcount = 0; // Number of active jobs + // Possible idle shutdown... + for (i = 0, count = cupsArrayGetCount(system->printers); i < count && jcount == 0; i ++) + { + printer = (pappl_printer_t *)cupsArrayGetElement(system->printers, i); - // Force shutdown after 60 seconds - if (system->shutdown_time && (time(NULL) - system->shutdown_time) > 60) + _papplRWLockRead(printer); + jcount += cupsArrayGetCount(printer->active_jobs); + _papplRWUnlock(printer); + } + + if (jcount == 0) { - _papplRWUnlock(system); - break; // Shutdown-System request + // No processing jobs, OK to idle exit... + _papplRWUnlock(system); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Idle shutdown."); + break; } + } - if (sigterm_time && (time(NULL) - sigterm_time) > 60) + if (system->shutdown_time || sigterm_time) + { + // Shutdown requested, see if we can do so safely... + if ((system->shutdown_time && (time(NULL) - system->shutdown_time) > 60) || (sigterm_time && (time(NULL) - sigterm_time) > 60)) { + // Force shutdown after 60 seconds _papplRWUnlock(system); - break; // SIGTERM received + break; } // Otherwise shutdown immediately if there are no more active jobs... - for (i = 0, count = cupsArrayGetCount(system->printers); i < count; i ++) + for (i = 0, count = cupsArrayGetCount(system->printers); i < count && jcount == 0; i ++) { printer = (pappl_printer_t *)cupsArrayGetElement(system->printers, i); diff --git a/pappl/system.h b/pappl/system.h index e320aabb..517c9421 100644 --- a/pappl/system.h +++ b/pappl/system.h @@ -169,6 +169,7 @@ extern char *papplSystemGetGeoLocation(pappl_system_t *system, char *buffer, si extern char *papplSystemGetHostname(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_DEPRECATED("Use papplSystemGetHostName instead."); extern char *papplSystemGetHostName(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern int papplSystemGetHostPort(pappl_system_t *system) _PAPPL_PUBLIC; +extern int papplSystemGetIdleShutdown(pappl_system_t *system) _PAPPL_PUBLIC; extern char *papplSystemGetLocation(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern pappl_loglevel_t papplSystemGetLogLevel(pappl_system_t *system) _PAPPL_PUBLIC; extern size_t papplSystemGetMaxClients(pappl_system_t *system) _PAPPL_PUBLIC; @@ -210,6 +211,7 @@ extern void papplSystemSetFooterHTML(pappl_system_t *system, const char *html) extern void papplSystemSetGeoLocation(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetHostname(pappl_system_t *system, const char *value) _PAPPL_DEPRECATED("Use papplSystemSetHostName instead."); extern void papplSystemSetHostName(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; +extern void papplSystemSetIdleShutdown(pappl_system_t *system, int seconds) _PAPPL_PUBLIC; extern void papplSystemSetLocation(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetLogLevel(pappl_system_t *system, pappl_loglevel_t loglevel) _PAPPL_PUBLIC; extern void papplSystemSetMaxClients(pappl_system_t *system, size_t max_clients) _PAPPL_PUBLIC; diff --git a/testsuite/testpappl.c b/testsuite/testpappl.c index 495e8772..61498ea9 100644 --- a/testsuite/testpappl.c +++ b/testsuite/testpappl.c @@ -1858,6 +1858,56 @@ test_api(pappl_system_t *system) // I - System else testEnd(true); + // papplSystemGet/SetIdleShutdown + testBegin("api: papplSystemGetIdleShutdown"); + if ((get_int = papplSystemGetIdleShutdown(system)) != 0) + { + testEndMessage(false, "got %d, expected 0", get_int); + pass = false; + } + else + testEnd(true); + + testBegin("api: papplSystemSetIdleShutdown(-1)"); + papplSystemSetIdleShutdown(system, -1); + if ((get_int = papplSystemGetIdleShutdown(system)) != 0) + { + testEndMessage(false, "got %d, expected 0", get_int); + pass = false; + } + else + testEnd(true); + + testBegin("api: papplSystemSetIdleShutdown(30)"); + papplSystemSetIdleShutdown(system, 30); + if ((get_int = papplSystemGetIdleShutdown(system)) != 30) + { + testEndMessage(false, "got %d, expected 30", get_int); + pass = false; + } + else + testEnd(true); + + testBegin("api: papplSystemSetIdleShutdown(0)"); + papplSystemSetIdleShutdown(system, 0); + if ((get_int = papplSystemGetIdleShutdown(system)) != 0) + { + testEndMessage(false, "got %d, expected 0", get_int); + pass = false; + } + else + testEnd(true); + + testBegin("api: papplSystemSetIdleShutdown(90)"); + papplSystemSetIdleShutdown(system, 90); + if ((get_int = papplSystemGetIdleShutdown(system)) != 90) + { + testEndMessage(false, "got %d, expected 30", get_int); + pass = false; + } + else + testEnd(true); + // papplSystemGet/SetLocation testBegin("api: papplSystemGetLocation"); if (!papplSystemGetLocation(system, get_str, sizeof(get_str))) @@ -3440,6 +3490,26 @@ test_client(pappl_system_t *system) // I - System testEnd(true); } + // Verify that idle shutdown works... + testBegin("client: Idle Shutdown"); + + end = time(NULL) + 100; + while (time(NULL) < end) + { + testProgress(); + sleep(5); + } + + if (papplSystemIsShutdown(system)) + { + testEnd(true); + } + else + { + testEnd(false); + goto done; + } + ret = true; // Clean up and return...