/*
- * "$Id: sysman.c 7928 2008-09-10 22:14:22Z mike $"
+ * System management functions for the CUPS scheduler.
*
- * System management functions for the CUPS scheduler.
+ * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2006 by Easy Software Products.
*
- * Copyright 2007-2011 by Apple Inc.
- * Copyright 2006 by Easy Software Products.
- *
- * These coded instructions, statements, and computer programs are the
- * property of Apple Inc. and are protected by Federal copyright
- * law. Distribution and use rights are outlined in the file "LICENSE.txt"
- * which should have been included with this file. If this file is
- * file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * Contents:
- *
- * cupsdCleanDirty() - Write dirty config and state files.
- * cupsdMarkDirty() - Mark config or state files as needing a
- * write.
- * cupsdSetBusyState() - Let the system know when we are busy
- * doing something.
- * cupsdAllowSleep() - Tell the OS it is now OK to sleep.
- * cupsdStartSystemMonitor() - Start monitoring for system change.
- * cupsdStopSystemMonitor() - Stop monitoring for system change.
- * sysEventThreadEntry() - A thread to receive power and computer
- * name change notifications.
- * sysEventPowerNotifier() - Handle power notification events.
- * sysEventConfigurationNotifier() - Computer name changed notification
- * callback.
- * sysEventTimerNotifier() - Handle delayed event notifications.
- * sysUpdate() - Update the current system state.
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
*/
*/
#include "cupsd.h"
-#ifdef HAVE_VPROC_TRANSACTION_BEGIN
-# include <vproc.h>
-#endif /* HAVE_VPROC_TRANSACTION_BEGIN */
#ifdef __APPLE__
+# include <xpc/xpc.h>
# include <IOKit/pwr_mgt/IOPMLib.h>
-# ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
-# include <IOKit/pwr_mgt/IOPMLibPrivate.h>
-# endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
#endif /* __APPLE__ */
/*
* The system management functions cover disk and power management which
- * are primarily used on portable computers.
+ * are primarily used for portable computers.
*
* Disk management involves delaying the write of certain configuration
* and state files to minimize the number of times the disk has to spin
- * up.
+ * up or flash to be written to.
*
- * Power management support is currently only implemented on MacOS X, but
+ * Power management support is currently only implemented on macOS, but
* essentially we use four functions to let the OS know when it is OK to
* put the system to sleep, typically when we are not in the middle of
- * printing a job.
- *
- * Once put to sleep, we invalidate all remote printers since it is common
- * to wake up in a new location/on a new wireless network.
- */
-
-/*
- * Local globals...
+ * printing a job. And on macOS we can also "sleep print" - basically the
+ * system only wakes up long enough to service network requests and process
+ * print jobs.
*/
-#ifdef kIOPMAssertionTypeDenySystemSleep
-static IOPMAssertionID dark_wake = 0; /* "Dark wake" assertion for sharing */
-#endif /* kIOPMAssertionTypeDenySystemSleep */
-
/*
* 'cupsdCleanDirty()' - Write dirty config and state files.
DirtyFiles = CUPSD_DIRTY_NONE;
DirtyCleanTime = 0;
- cupsdSetBusyState();
+ cupsdSetBusyState(0);
}
if (!DirtyCleanTime)
DirtyCleanTime = time(NULL) + DirtyCleanInterval;
- cupsdSetBusyState();
+ cupsdSetBusyState(0);
}
*/
void
-cupsdSetBusyState(void)
+cupsdSetBusyState(int working) /* I - Doing significant work? */
{
int i; /* Looping var */
cupsd_job_t *job; /* Current job */
"Active clients and printing jobs",
"Active clients, printing jobs, and dirty files"
};
-#ifdef HAVE_VPROC_TRANSACTION_BEGIN
- static vproc_transaction_t vtran = 0; /* Current busy transaction */
-#endif /* HAVE_VPROC_TRANSACTION_BEGIN */
+#ifdef __APPLE__
+ static int tran = 0; /* Current busy transaction */
+ static IOPMAssertionID keep_awake = 0;/* Keep the system awake while printing */
+#endif /* __APPLE__ */
/*
*/
newbusy = (DirtyCleanTime ? 1 : 0) |
- (cupsArrayCount(ActiveClients) ? 4 : 0);
+ ((working || cupsArrayCount(ActiveClients) > 0) ? 4 : 0);
for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs);
job;
{
busy = newbusy;
-#ifdef HAVE_VPROC_TRANSACTION_BEGIN
- if (busy && !vtran)
- vtran = vproc_transaction_begin(NULL);
- else if (!busy && vtran)
+#ifdef __APPLE__
+ if (busy && !tran)
+ {
+ xpc_transaction_begin();
+ tran = 1;
+ }
+ else if (!busy && tran)
{
- vproc_transaction_end(NULL, vtran);
- vtran = 0;
+ xpc_transaction_end();
+ tran = 0;
}
-#endif /* HAVE_VPROC_TRANSACTION_BEGIN */
+#endif /* __APPLE__ */
}
-#ifdef kIOPMAssertionTypeDenySystemSleep
- if (cupsArrayCount(PrintingJobs) > 0 && !dark_wake)
+#ifdef __APPLE__
+ if (cupsArrayCount(PrintingJobs) > 0 && !keep_awake)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG, "Asserting dark wake.");
- IOPMAssertionCreateWithName(kIOPMAssertionTypeDenySystemSleep,
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Asserting NetworkClientActive.");
+
+ IOPMAssertionCreateWithName(kIOPMAssertNetworkClientActive,
kIOPMAssertionLevelOn,
- CFSTR("org.cups.cupsd"), &dark_wake);
+ CFSTR("org.cups.cupsd"), &keep_awake);
}
- else if (cupsArrayCount(PrintingJobs) == 0 && dark_wake)
+ else if (cupsArrayCount(PrintingJobs) == 0 && keep_awake)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG, "Releasing dark wake assertion.");
- IOPMAssertionRelease(dark_wake);
- dark_wake = 0;
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Releasing power assertion.");
+ IOPMAssertionRelease(keep_awake);
+ keep_awake = 0;
}
-#endif /* kIOPMAssertionTypeDenySystemSleep */
+#endif /* __APPLE__ */
}
* Include MacOS-specific headers...
*/
+# include <notify.h>
# include <IOKit/IOKitLib.h>
# include <IOKit/IOMessage.h>
+# include <IOKit/ps/IOPowerSources.h>
# include <IOKit/pwr_mgt/IOPMLib.h>
# include <SystemConfiguration/SystemConfiguration.h>
# include <pthread.h>
NetworkInterfaceKeyIPv6 = NULL;
/* Netowrk interface key */
static cupsd_sysevent_t LastSysEvent; /* Last system event (for delayed sleep) */
+static int NameChanged = 0;/* Did we get a 'name changed' event during sleep? */
+static int PSToken = 0; /* Power source notifications */
/*
void *context);
static void sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context);
static void sysUpdate(void);
+static void sysUpdateNames(void);
/*
{
cupsdCleanDirty();
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
IOAllowPowerChange(LastSysEvent.powerKernelPort,
LastSysEvent.powerNotificationID);
}
int flags; /* fcntl flags on pipe */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor()");
+
if (cupsdOpenPipe(SysEventPipes))
{
cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!",
pthread_mutex_init(&SysEventThreadMutex, NULL);
pthread_cond_init(&SysEventThreadCond, NULL);
- pthread_create(&SysEventThread, NULL, (void *(*)())sysEventThreadEntry, NULL);
+ pthread_create(&SysEventThread, NULL, (void *(*)(void *))sysEventThreadEntry, NULL);
+
+ /*
+ * Monitor for power source changes via dispatch queue...
+ */
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor: IOPSGetTimeRemainingEstimate=%f", IOPSGetTimeRemainingEstimate());
+ ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited;
+ notify_register_dispatch(kIOPSNotifyPowerSource, &PSToken, dispatch_get_main_queue(), ^(int t) { (void)t;
+ ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; });
}
CFRunLoopRef rl; /* The event handler runloop */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSystemMonitor()");
+
if (SysEventThread)
{
/*
cupsdRemoveSelect(SysEventPipes[0]);
cupsdClosePipe(SysEventPipes);
}
+
+ if (PSToken != 0)
+ {
+ notify_cancel(PSToken);
+ PSToken = 0;
+ }
}
switch (messageType)
{
- case kIOMessageCanSystemPowerOff:
- case kIOMessageCanSystemSleep:
+ case kIOMessageCanSystemPowerOff :
+ case kIOMessageCanSystemSleep :
threadData->sysevent.event |= SYSEVENT_CANSLEEP;
break;
- case kIOMessageSystemWillRestart:
- case kIOMessageSystemWillPowerOff:
- case kIOMessageSystemWillSleep:
+ case kIOMessageSystemWillRestart :
+ case kIOMessageSystemWillPowerOff :
+ case kIOMessageSystemWillSleep :
threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
+ threadData->sysevent.event &= ~SYSEVENT_WOKE;
break;
- case kIOMessageSystemHasPoweredOn:
+ case kIOMessageSystemHasPoweredOn :
/*
* Because powered on is followed by a net-changed event, delay
* before sending it.
threadData->sysevent.event |= SYSEVENT_WOKE;
break;
- case kIOMessageSystemWillNotPowerOff:
- case kIOMessageSystemWillNotSleep:
+ case kIOMessageSystemWillNotPowerOff :
+ case kIOMessageSystemWillNotSleep :
# ifdef kIOMessageSystemWillPowerOn
- case kIOMessageSystemWillPowerOn:
+ case kIOMessageSystemWillPowerOn :
# endif /* kIOMessageSystemWillPowerOn */
default:
sendit = 0;
break;
}
+ switch (messageType)
+ {
+ case kIOMessageCanSystemPowerOff :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageCanSystemPowerOff message.");
+ break;
+ case kIOMessageCanSystemSleep :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageCannSystemSleep message.");
+ break;
+ case kIOMessageSystemWillRestart :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemWillRestart message.");
+ break;
+ case kIOMessageSystemWillPowerOff :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemWillPowerOff message.");
+ break;
+ case kIOMessageSystemWillSleep :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemWillSleep message.");
+ break;
+ case kIOMessageSystemHasPoweredOn :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemHasPoweredOn message.");
+ break;
+ case kIOMessageSystemWillNotPowerOff :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemWillNotPowerOff message.");
+ break;
+ case kIOMessageSystemWillNotSleep :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemWillNotSleep message.");
+ break;
+# ifdef kIOMessageSystemWillPowerOn
+ case kIOMessageSystemWillPowerOn :
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Got kIOMessageSystemWillPowerOn message.");
+ break;
+# endif /* kIOMessageSystemWillPowerOn */
+ default:
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Got unknown power message %d.",
+ (int)messageType);
+ break;
+ }
+
if (sendit == 0)
IOAllowPowerChange(threadData->sysevent.powerKernelPort,
(long)messageArgument);
{
/*
* If there are active printers that don't have the connecting-to-device
- * printer-state-reason then cancel the sleep request (i.e. this reason
- * indicates a job that is not yet connected to the printer)...
+ * or cups-waiting-for-job-completed printer-state-reason then cancel the
+ * sleep request, i.e., these reasons indicate a job that is not actively
+ * doing anything...
*/
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
if (p->job)
{
for (i = 0; i < p->num_reasons; i ++)
- if (!strcmp(p->reasons[i], "connecting-to-device"))
+ if (!strcmp(p->reasons[i], "connecting-to-device") ||
+ !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
break;
if (!p->num_reasons || i >= p->num_reasons)
if (p)
{
cupsdLogMessage(CUPSD_LOG_INFO,
- "System sleep canceled because printer %s is active",
+ "System sleep canceled because printer %s is active.",
p->name);
IOCancelPowerChange(sysevent.powerKernelPort,
sysevent.powerNotificationID);
}
else
{
- cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep");
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep.");
IOAllowPowerChange(sysevent.powerKernelPort,
sysevent.powerNotificationID);
}
if (sysevent.event & SYSEVENT_WILLSLEEP)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep");
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep.");
Sleeping = 1;
- for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
- p;
- p = (cupsd_printer_t *)cupsArrayNext(Printers))
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "Deregistering local printer \"%s\"", p->name);
- cupsdDeregisterPrinter(p, 0);
- }
-
cupsdCleanDirty();
-#ifdef kIOPMAssertionTypeDenySystemSleep
- /*
- * Tell the OS it is OK to sleep when we remove our assertion...
- */
-
- IOAllowPowerChange(sysevent.powerKernelPort,
- sysevent.powerNotificationID);
-
-#else
/*
* If we have no printing jobs, allow the power change immediately.
- * Otherwise set the SleepJobs time to 15 seconds in the future when
+ * Otherwise set the SleepJobs time to 10 seconds in the future when
* we'll take more drastic measures...
*/
if (cupsArrayCount(PrintingJobs) == 0)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
IOAllowPowerChange(sysevent.powerKernelPort,
sysevent.powerNotificationID);
+ }
else
{
/*
* If there are active printers that don't have the connecting-to-device
- * printer-state-reason then delay the sleep request (i.e. this reason
- * indicates a job that is not yet connected to the printer)...
+ * or cups-waiting-for-job-completed printer-state-reasons then delay the
+ * sleep request, i.e., these reasons indicate a job is active...
*/
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
if (p->job)
{
for (i = 0; i < p->num_reasons; i ++)
- if (!strcmp(p->reasons[i], "connecting-to-device"))
+ if (!strcmp(p->reasons[i], "connecting-to-device") ||
+ !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
break;
if (!p->num_reasons || i >= p->num_reasons)
if (p)
{
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "System sleep delayed because printer %s is active.",
+ p->name);
+
LastSysEvent = sysevent;
SleepJobs = time(NULL) + 10;
}
else
{
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
IOAllowPowerChange(sysevent.powerKernelPort,
sysevent.powerNotificationID);
}
}
-#endif /* kIOPMAssertionTypeDenySystemSleep */
}
if (sysevent.event & SYSEVENT_WOKE)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep");
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep.");
IOAllowPowerChange(sysevent.powerKernelPort,
sysevent.powerNotificationID);
Sleeping = 0;
+
+ /*
+ * Make sure jobs that were queued prior to the system going to sleep don't
+ * get canceled right away...
+ */
+
+ if (MaxJobTime > 0)
+ {
+ cupsd_job_t *job; /* Current job */
+
+ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
+ job;
+ job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
+ {
+ if (job->cancel_time)
+ {
+ ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs,
+ "job-cancel-after",
+ IPP_TAG_INTEGER);
+
+ if (cancel_after)
+ job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0);
+ else
+ job->cancel_time = time(NULL) + MaxJobTime;
+ }
+ }
+ }
+
+ if (NameChanged)
+ sysUpdateNames();
+
cupsdCheckJobs();
}
if (sysevent.event & SYSEVENT_NETCHANGED)
{
- if (!Sleeping)
+ if (Sleeping)
cupsdLogMessage(CUPSD_LOG_DEBUG,
- "System network configuration changed");
+ "System network configuration changed - "
+ "ignored while sleeping.");
else
cupsdLogMessage(CUPSD_LOG_DEBUG,
- "System network configuration changed; "
- "ignored while sleeping");
+ "System network configuration changed.");
}
if (sysevent.event & SYSEVENT_NAMECHANGED)
{
- if (!Sleeping)
+ if (Sleeping)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "Computer name or BTMM domains changed");
-
- /*
- * De-register the individual printers...
- */
+ NameChanged = 1;
- for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
- p;
- p = (cupsd_printer_t *)cupsArrayNext(Printers))
- cupsdDeregisterPrinter(p, 1);
-
- /*
- * Update the computer name and BTMM domain list...
- */
-
- cupsdUpdateDNSSDName();
-
- /*
- * Now re-register them...
- */
-
- for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
- p;
- p = (cupsd_printer_t *)cupsArrayNext(Printers))
- cupsdRegisterPrinter(p);
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "Computer name or BTMM domains changed - ignored while "
+ "sleeping.");
}
else
+ {
cupsdLogMessage(CUPSD_LOG_DEBUG,
- "Computer name or BTMM domains changed; ignored while "
- "sleeping");
+ "Computer name or BTMM domains changed.");
+
+ sysUpdateNames();
+ }
}
}
}
-#endif /* __APPLE__ */
/*
- * End of "$Id: sysman.c 7928 2008-09-10 22:14:22Z mike $".
+ * 'sysUpdateNames()' - Update computer and/or BTMM domains.
*/
+
+static void
+sysUpdateNames(void)
+{
+ cupsd_printer_t *p; /* Current printer */
+
+
+ NameChanged = 0;
+
+ /*
+ * De-register the individual printers...
+ */
+
+ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+ p;
+ p = (cupsd_printer_t *)cupsArrayNext(Printers))
+ cupsdDeregisterPrinter(p, 1);
+
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ /*
+ * Update the computer name and BTMM domain list...
+ */
+
+ cupsdUpdateDNSSDName();
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ /*
+ * Now re-register them...
+ */
+
+ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+ p;
+ p = (cupsd_printer_t *)cupsArrayNext(Printers))
+ cupsdRegisterPrinter(p);
+}
+#endif /* __APPLE__ */