- Added `NoSystem` SSLOptions value (Issue #1130)
- The scheduler now logs a job's debugging history if the backend fails
(Issue #1205)
+- Fixed a potential timing issue with `cupsEnumDests` (Issue #1084)
- Fixed a potential "lost PPD" condition in the scheduler (Issue #1109)
- Fixed a compressed file error handling bug (Issue #1070)
- Fixed a bug in the make-and-model whitespace trimming code (Issue #1096)
#
# Library Makefile for CUPS.
#
-# Copyright © 2022-2024 by OpenPrinting.
+# Copyright © 2022-2025 by OpenPrinting.
# Copyright © 2007-2021 by Apple Inc.
# Copyright © 1997-2006 by Easy Software Products, all rights reserved.
#
COREOBJS = \
array.o \
auth.o \
+ clock.o \
debug.o \
dest.o \
dest-job.o \
--- /dev/null
+//
+// Monotonic clock API for CUPS.
+//
+// Copyright © 2024-2025 by OpenPrinting.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "cups-private.h"
+
+
+//
+// Local globals...
+//
+
+static bool cups_clock_init = false;// Clock initialized?
+static _cups_mutex_t cups_clock_mutex = _CUPS_MUTEX_INITIALIZER;
+ // Mutex to control access
+#ifdef _WIN32
+static ULONGLONG cups_first_tick; // First tick count
+#else
+# if defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW)
+static struct timespec cups_first_clock;// First clock value
+# endif // CLOCK_MONOTONIC || CLOCK_MONOTONIC_RAW
+static struct timeval cups_first_time; // First time value
+#endif // _WIN32
+
+
+//
+// '_cupsGetClock()' - Get a monotonic clock value in seconds.
+//
+// This function returns a monotonically increasing clock value in seconds. The
+// first call will always return 0.0. Subsequent calls will return the number
+// of seconds that have elapsed since the first call, regardless of system time
+// changes, sleep, etc. The sub-second accuracy varies based on the operating
+// system and hardware but is typically 10ms or better.
+//
+
+double // O - Elapsed seconds
+_cupsGetClock(void)
+{
+ double secs; // Elapsed seconds
+#ifdef _WIN32
+ ULONGLONG curtick; // Current tick count
+#else
+# ifdef CLOCK_MONOTONIC
+ struct timespec curclock; // Current clock value
+# endif // CLOCK_MONOTONIC
+ struct timeval curtime; // Current time value
+#endif // _WIN32
+
+
+ _cupsMutexLock(&cups_clock_mutex);
+
+#ifdef _WIN32
+ // Get the current tick count in milliseconds...
+ curtick = GetTickCount64();
+
+ if (!cups_clock_init)
+ {
+ // First time through initialize the initial tick count...
+ cups_clock_init = true;
+ cups_first_tick = curtick;
+ }
+
+ // Convert ticks to seconds...
+ if (curtick < cups_first_tick)
+ secs = 0.0;
+ else
+ secs = 0.001 * (curtick - cups_first_tick);
+
+#else
+# if defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW)
+ // Get the current tick count in milliseconds...
+# ifdef CLOCK_MONOTONIC_RAW
+ if (!clock_gettime(CLOCK_MONOTONIC_RAW, &curclock))
+# else
+ if (!clock_gettime(CLOCK_MONOTONIC, &curclock))
+# endif // CLOCK_MONOTONIC_RAW
+ {
+ if (!cups_clock_init)
+ {
+ // First time through initialize the initial clock value...
+ cups_clock_init = true;
+ cups_first_clock = curclock;
+ }
+
+ // Convert clock value to seconds...
+ if ((secs = curclock.tv_sec - cups_first_clock.tv_sec + 0.000000001 * (curclock.tv_nsec - cups_first_clock.tv_nsec)) < 0.0)
+ secs = 0.0;
+ }
+ else
+# endif // CLOCK_MONOTONIC || CLOCK_MONOTONIC_RAW
+ {
+ gettimeofday(&curtime, /*tzp*/NULL);
+
+ if (!cups_clock_init)
+ {
+ // First time through initialize the initial clock value...
+ cups_clock_init = true;
+ cups_first_time = curtime;
+ }
+
+ // Convert time value to seconds...
+ if ((secs = curtime.tv_sec - cups_first_time.tv_sec + 0.000001 * (curtime.tv_usec - cups_first_time.tv_usec)) < 0.0)
+ secs = 0.0;
+ }
+#endif // _WIN32
+
+ _cupsMutexUnlock(&cups_clock_mutex);
+
+ return (secs);
+}
/*
* Private definitions for CUPS.
*
- * Copyright © 2020-2024 by OpenPrinting.
+ * Copyright © 2020-2025 by OpenPrinting.
* Copyright © 2007-2019 by Apple Inc.
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
*
extern char *_cupsCreateDest(const char *name, const char *info, const char *device_id, const char *device_uri, char *uri, size_t urisize) _CUPS_PRIVATE;
extern ipp_attribute_t *_cupsEncodeOption(ipp_t *ipp, ipp_tag_t group_tag, _ipp_option_t *map, const char *name, const char *value) _CUPS_PRIVATE;
extern int _cupsGet1284Values(const char *device_id, cups_option_t **values) _CUPS_PRIVATE;
+extern double _cupsGetClock(void) _CUPS_PRIVATE;
extern const char *_cupsGetDestResource(cups_dest_t *dest, unsigned flags, char *resource, size_t resourcesize) _CUPS_PRIVATE;
extern int _cupsGetDests(http_t *http, ipp_op_t op, const char *name, cups_dest_t **dests, cups_ptype_t type, cups_ptype_t mask) _CUPS_PRIVATE;
extern const char *_cupsGetPassword(const char *prompt) _CUPS_PRIVATE;
/*
* User-defined destination (and option) support for CUPS.
*
- * Copyright © 2020-2024 by OpenPrinting.
+ * Copyright © 2020-2025 by OpenPrinting.
* Copyright © 2007-2019 by Apple Inc.
* Copyright © 1997-2007 by Easy Software Products.
*
* information.
*/
-/*
- * Include necessary headers...
- */
-
#include "cups-private.h"
#include "debug-internal.h"
#include <sys/stat.h>
typedef struct _cups_dnssd_resolve_s /* Data for resolving URI */
{
int *cancel; /* Pointer to "cancel" variable */
- struct timeval end_time; /* Ending time */
+ double end_time; /* Ending time */
} _cups_dnssd_resolve_t;
#endif /* HAVE_DNSSD */
static int cups_dnssd_resolve_cb(void *context);
static void cups_dnssd_unquote(char *dst, const char *src,
size_t dstsize);
-static int cups_elapsed(struct timeval *t);
+static int cups_elapsed(double *t);
#endif /* HAVE_DNSSD */
static int cups_enum_dests(http_t *http, unsigned flags, int msec, int *cancel, cups_ptype_t type, cups_ptype_t mask, cups_dest_cb_t cb, void *user_data);
static int cups_find_dest(const char *name, const char *instance,
* Resolve the URI...
*/
- resolve.cancel = cancel;
- gettimeofday(&resolve.end_time, NULL);
+ resolve.cancel = cancel;
+ resolve.end_time = _cupsGetClock();
if (msec > 0)
- {
- resolve.end_time.tv_sec += msec / 1000;
- resolve.end_time.tv_usec += (msec % 1000) * 1000;
-
- while (resolve.end_time.tv_usec >= 1000000)
- {
- resolve.end_time.tv_sec ++;
- resolve.end_time.tv_usec -= 1000000;
- }
- }
+ resolve.end_time += 0.001 * msec;
else
- resolve.end_time.tv_sec += 75;
+ resolve.end_time += 75.0;
if (cb)
(*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_RESOLVING, dest);
{
_cups_dnssd_resolve_t *resolve = (_cups_dnssd_resolve_t *)context;
/* Resolve data */
- struct timeval curtime; /* Current time */
+ double curtime; /* Current time */
/*
* Otherwise check the end time...
*/
- gettimeofday(&curtime, NULL);
+ curtime = _cupsGetClock();
- DEBUG_printf(("4cups_dnssd_resolve_cb: curtime=%d.%06d, end_time=%d.%06d", (int)curtime.tv_sec, (int)curtime.tv_usec, (int)resolve->end_time.tv_sec, (int)resolve->end_time.tv_usec));
+ DEBUG_printf(("4cups_dnssd_resolve_cb: curtime=%.6f, end_time=%.6f", curtime, resolve->end_time));
- return (curtime.tv_sec < resolve->end_time.tv_sec ||
- (curtime.tv_sec == resolve->end_time.tv_sec &&
- curtime.tv_usec < resolve->end_time.tv_usec));
+ return (curtime < resolve->end_time);
}
*/
static int /* O - Elapsed time in milliseconds */
-cups_elapsed(struct timeval *t) /* IO - Previous time */
+cups_elapsed(double *t) /* IO - Previous time */
{
- int msecs; /* Milliseconds */
- struct timeval nt; /* New time */
+ int msecs; /* Milliseconds */
+ double nt; /* New time */
- gettimeofday(&nt, NULL);
+ nt = _cupsGetClock();
- msecs = (int)(1000 * (nt.tv_sec - t->tv_sec) + (nt.tv_usec - t->tv_usec) / 1000);
+ msecs = (int)(1000.0 * (nt - *t));
*t = nt;
int count, /* Number of queries started */
completed, /* Number of completed queries */
remaining; /* Remainder of timeout */
- struct timeval curtime; /* Current time */
+ double curtime; /* Current time */
_cups_dnssd_data_t data; /* Data for callback */
_cups_dnssd_device_t *device; /* Current device */
# ifdef HAVE_MDNSRESPONDER
* Get Bonjour-shared printers...
*/
- gettimeofday(&curtime, NULL);
+ curtime = _cupsGetClock();
# ifdef HAVE_MDNSRESPONDER
if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)