Coverage Report

Created: 2025-10-10 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/globals.c
Line
Count
Source
1
//
2
// Global variable access routines for CUPS.
3
//
4
// Copyright © 2021-2025 by OpenPrinting.
5
// Copyright © 2007-2019 by Apple Inc.
6
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7
//
8
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
9
// information.
10
//
11
12
#include "cups-private.h"
13
#include "debug-internal.h"
14
#ifndef _WIN32
15
#  include <pwd.h>
16
#  ifdef HAVE_SYS_AUXV_H
17
#    include <sys/auxv.h> // for getauxval()
18
#  endif
19
#endif /* !_WIN32 */
20
21
22
/*
23
 * Local globals...
24
 */
25
26
#ifdef DEBUG
27
static int    cups_global_index = 0;
28
          /* Next thread number */
29
#endif /* DEBUG */
30
static cups_thread_key_t cups_globals_key = CUPS_THREADKEY_INITIALIZER;
31
          /* Thread local storage key */
32
#ifdef HAVE_PTHREAD_H
33
static pthread_once_t cups_globals_key_once = PTHREAD_ONCE_INIT;
34
          /* One-time initialization object */
35
#endif /* HAVE_PTHREAD_H */
36
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
37
static cups_mutex_t cups_global_mutex = CUPS_MUTEX_INITIALIZER;
38
          /* Global critical section */
39
#endif /* HAVE_PTHREAD_H || _WIN32 */
40
41
42
/*
43
 * Local functions...
44
 */
45
46
#ifdef _WIN32
47
static void   cups_fix_path(char *path);
48
#endif /* _WIN32 */
49
static _cups_globals_t  *cups_globals_alloc(void);
50
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
51
static void   cups_globals_free(_cups_globals_t *g);
52
#endif /* HAVE_PTHREAD_H || _WIN32 */
53
#ifdef HAVE_PTHREAD_H
54
static void   cups_globals_init(void);
55
#endif /* HAVE_PTHREAD_H */
56
57
58
/*
59
 * '_cupsGlobalLock()' - Lock the global mutex.
60
 */
61
62
void
63
_cupsGlobalLock(void)
64
0
{
65
0
  cupsMutexLock(&cups_global_mutex);
66
0
}
67
68
69
/*
70
 * '_cupsGlobals()' - Return a pointer to thread local storage
71
 */
72
73
_cups_globals_t *     /* O - Pointer to global data */
74
_cupsGlobals(void)
75
0
{
76
0
  _cups_globals_t *cg;      /* Pointer to global data */
77
78
79
0
#ifdef HAVE_PTHREAD_H
80
 /*
81
  * Initialize the global data exactly once...
82
  */
83
84
0
  pthread_once(&cups_globals_key_once, cups_globals_init);
85
0
#endif /* HAVE_PTHREAD_H */
86
87
 /*
88
  * See if we have allocated the data yet...
89
  */
90
91
0
  if ((cg = (_cups_globals_t *)cupsThreadGetData(cups_globals_key)) == NULL)
92
0
  {
93
   /*
94
    * No, allocate memory as set the pointer for the key...
95
    */
96
97
0
    if ((cg = cups_globals_alloc()) != NULL)
98
0
      cupsThreadSetData(cups_globals_key, cg);
99
0
  }
100
101
 /*
102
  * Return the pointer to the data...
103
  */
104
105
0
  return (cg);
106
0
}
107
108
109
/*
110
 * '_cupsGlobalUnlock()' - Unlock the global mutex.
111
 */
112
113
void
114
_cupsGlobalUnlock(void)
115
0
{
116
0
  cupsMutexUnlock(&cups_global_mutex);
117
0
}
118
119
120
#ifdef _WIN32
121
/*
122
 * 'DllMain()' - Main entry for library.
123
 *
124
 * @private@
125
 */
126
127
BOOL WINAPI       /* O - Success/failure */
128
DllMain(HINSTANCE hinst,    /* I - DLL module handle */
129
        DWORD     reason,   /* I - Reason */
130
        LPVOID    reserved)   /* I - Unused */
131
{
132
  _cups_globals_t *cg;      /* Global data */
133
134
135
  (void)hinst;
136
  (void)reserved;
137
138
  switch (reason)
139
  {
140
    case DLL_PROCESS_ATTACH :   /* Called on library initialization */
141
        cupsMutexInit(&cups_global_mutex);
142
143
        if ((cups_globals_key = TlsAlloc()) == TLS_OUT_OF_INDEXES)
144
          return (FALSE);
145
        break;
146
147
    case DLL_THREAD_DETACH :    /* Called when a thread terminates */
148
        if ((cg = (_cups_globals_t *)TlsGetValue(cups_globals_key)) != NULL)
149
          cups_globals_free(cg);
150
        break;
151
152
    case DLL_PROCESS_DETACH :   /* Called when library is unloaded */
153
        if ((cg = (_cups_globals_t *)TlsGetValue(cups_globals_key)) != NULL)
154
          cups_globals_free(cg);
155
156
        TlsFree(cups_globals_key);
157
  cupsMutexDestroy(&cups_global_mutex);
158
        break;
159
160
    default:
161
        break;
162
  }
163
164
  return (TRUE);
165
}
166
#endif /* _WIN32 */
167
168
169
/*
170
 * 'cups_globals_alloc()' - Allocate and initialize global data.
171
 */
172
173
static _cups_globals_t *    /* O - Pointer to global data */
174
cups_globals_alloc(void)
175
0
{
176
0
  const char  *cups_userconfig = getenv("CUPS_USERCONFIG");
177
          /* Location of user config files */
178
0
  _cups_globals_t *cg = calloc(1, sizeof(_cups_globals_t));
179
          /* Pointer to global data */
180
181
182
0
  if (!cg)
183
0
    return (NULL);
184
185
 /*
186
  * Clear the global storage and set the default encryption and password
187
  * callback values...
188
  */
189
190
0
  cg->encryption     = (http_encryption_t)-1;
191
0
  cg->password_cb    = (cups_password_cb2_t)_cupsGetPassword;
192
0
  cg->trust_first    = -1;
193
0
  cg->any_root       = -1;
194
0
  cg->expired_certs  = -1;
195
0
  cg->validate_certs = -1;
196
197
#ifdef DEBUG
198
 /*
199
  * Friendly thread ID for debugging...
200
  */
201
202
  cg->thread_id = ++ cups_global_index;
203
#endif /* DEBUG */
204
205
 /*
206
  * Then set directories as appropriate...
207
  */
208
209
#ifdef _WIN32
210
  HKEY    key;      /* Registry key */
211
  DWORD   size;     /* Size of string */
212
  static char installdir[1024] = "",  /* Install directory */
213
    localedir[1024] = "", /* Locale directory */
214
    sysconfig[1024] = "", /* Server configuration directory */
215
    userconfig[1024] = "";  /* User configuration directory */
216
217
  if (!installdir[0])
218
  {
219
   /*
220
    * Open the registry...
221
    */
222
223
    cupsCopyString(installdir, "C:/Program Files/cups.org", sizeof(installdir));
224
225
    if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\cups.org", 0, KEY_READ, &key))
226
    {
227
     /*
228
      * Grab the installation directory...
229
      */
230
231
      char  *ptr;     /* Pointer into installdir */
232
233
      size = sizeof(installdir);
234
      RegQueryValueExA(key, "installdir", NULL, NULL, installdir, &size);
235
      RegCloseKey(key);
236
237
      for (ptr = installdir; *ptr;)
238
      {
239
        if (*ptr == '\\')
240
        {
241
          if (ptr[1])
242
            *ptr++ = '/';
243
          else
244
            *ptr = '\0';    /* Strip trailing \ */
245
        }
246
        else if (*ptr == '/' && !ptr[1])
247
          *ptr = '\0';      /* Strip trailing / */
248
        else
249
          ptr ++;
250
      }
251
    }
252
253
    snprintf(sysconfig, sizeof(sysconfig), "%s/conf", installdir);
254
    snprintf(localedir, sizeof(localedir), "%s/locale", installdir);
255
  }
256
257
  if ((cg->cups_datadir = getenv("CUPS_DATADIR")) == NULL)
258
    cg->cups_datadir = installdir;
259
260
  if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
261
    cg->cups_serverbin = installdir;
262
263
  if ((cg->sysconfig = getenv("CUPS_SERVERROOT")) == NULL)
264
    cg->sysconfig = sysconfig;
265
266
  if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
267
    cg->cups_statedir = sysconfig;
268
269
  if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
270
    cg->localedir = localedir;
271
272
  if (cups_userconfig)
273
  {
274
    // Use CUPS_USERCONFIG environment variable...
275
    cg->userconfig = cups_userconfig;
276
  }
277
  else
278
  {
279
    // Use the USERPROFILE environment variable to find the user configuration directory...
280
    if (!userconfig[0])
281
    {
282
      const char  *userprofile = getenv("USERPROFILE");
283
        // User profile (home) directory
284
      char  *userptr; // Pointer into userconfig
285
286
      snprintf(userconfig, sizeof(userconfig), "%s/AppData/Local/cups", userprofile);
287
      for (userptr = userconfig; *userptr; userptr ++)
288
      {
289
  // Convert back slashes to forward slashes
290
  if (*userptr == '\\')
291
    *userptr = '/';
292
      }
293
    }
294
295
    cg->userconfig = userconfig;
296
  }
297
298
#else
299
0
  const char  *home = getenv("HOME"); // HOME environment variable
300
0
  char    homedir[1024],    // Home directory from account
301
0
    temp[1024];   // Temporary directory string
302
0
#  ifndef __APPLE__
303
0
  const char  *snap_common = getenv("SNAP_COMMON"),
304
0
    *xdg_config_home = getenv("XDG_CONFIG_HOME");
305
          // Environment variables
306
0
#  endif // !__APPLE__
307
0
#  if defined(HAVE_SYS_AUXV_H) && defined(AT_SECURE)
308
0
  if (getauxval(AT_SECURE))
309
#  elif defined(HAVE_GETEUID)
310
  if ((geteuid() != getuid() && getuid()) || getegid() != getgid())
311
#  else
312
  if (!getuid())
313
#  endif /* HAVE_GETEUID */
314
0
  {
315
   /*
316
    * When running setuid/setgid, don't allow environment variables to override
317
    * the directories...
318
    */
319
320
0
    cg->cups_datadir    = CUPS_DATADIR;
321
0
    cg->cups_serverbin  = CUPS_SERVERBIN;
322
0
    cg->sysconfig       = CUPS_SERVERROOT;
323
0
    cg->cups_statedir   = CUPS_STATEDIR;
324
0
    cg->localedir       = CUPS_LOCALEDIR;
325
326
0
    cups_userconfig = NULL;
327
0
  }
328
0
  else
329
0
  {
330
   /*
331
    * Allow directories to be overridden by environment variables.
332
    */
333
334
0
    if ((cg->cups_datadir = getenv("CUPS_DATADIR")) == NULL)
335
0
      cg->cups_datadir = CUPS_DATADIR;
336
337
0
    if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
338
0
      cg->cups_serverbin = CUPS_SERVERBIN;
339
340
0
    if ((cg->sysconfig = getenv("CUPS_SYSCONFIG")) == NULL)
341
0
    {
342
0
      if ((cg->sysconfig = getenv("CUPS_SERVERROOT")) == NULL)
343
0
  cg->sysconfig = CUPS_SERVERROOT;
344
0
    }
345
346
0
    if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
347
0
      cg->cups_statedir = CUPS_STATEDIR;
348
349
0
    if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
350
0
      cg->localedir = CUPS_LOCALEDIR;
351
0
  }
352
353
0
  if (!getuid())
354
0
  {
355
    // When running as root, make "userconfig" the same as "sysconfig"...
356
0
    cg->userconfig = strdup(cg->sysconfig);
357
0
    return (cg);
358
0
  }
359
0
  else if (cups_userconfig)
360
0
  {
361
    // Use the value of the CUPS_USERCONFIG environment variable...
362
0
    cg->userconfig = strdup(cups_userconfig);
363
0
    return (cg);
364
0
  }
365
366
  // Find the user configuration directory relative to the home directory...
367
#  ifdef __APPLE__
368
  if (!home)
369
#else
370
0
  if (!home && !xdg_config_home)
371
0
#  endif // __APPLE__
372
0
  {
373
0
    struct passwd pw;   /* User info */
374
0
    struct passwd *result;  /* Auxiliary pointer */
375
376
0
    getpwuid_r(getuid(), &pw, cg->pw_buf, PW_BUF_SIZE, &result);
377
0
    if (result)
378
0
    {
379
0
      cupsCopyString(homedir, pw.pw_dir, sizeof(homedir));
380
0
      home = homedir;
381
0
    }
382
0
  }
383
384
#  ifdef __APPLE__
385
  if (home)
386
  {
387
    // macOS uses ~/Library/Application Support/FOO
388
    snprintf(temp, sizeof(temp), "%s/Library/Application Support/cups", home);
389
  }
390
  else
391
  {
392
    // Something went wrong, use temporary directory...
393
    snprintf(temp, sizeof(temp), "/private/tmp/cups%u", (unsigned)getuid());
394
  }
395
396
#  else
397
0
  if (snap_common)
398
0
  {
399
    // Snaps use $SNAP_COMMON/FOO
400
0
    snprintf(temp, sizeof(temp), "%s/cups", snap_common);
401
0
  }
402
0
  else if (xdg_config_home)
403
0
  {
404
    // XDG uses $XDG_CONFIG_HOME/FOO
405
0
    snprintf(temp, sizeof(temp), "%s/cups", xdg_config_home);
406
0
  }
407
0
  else if (home)
408
0
  {
409
    // Use ~/.cups if it exists, otherwise ~/.config/cups (XDG standard)
410
0
    snprintf(temp, sizeof(temp), "%s/.cups", home);
411
0
    if (access(temp, 0))
412
0
      snprintf(temp, sizeof(temp), "%s/.config/cups", home);
413
0
  }
414
0
  else
415
0
  {
416
    // Something went wrong, use temporary directory...
417
0
    snprintf(temp, sizeof(temp), "/tmp/cups%u", (unsigned)getuid());
418
0
  }
419
0
#  endif // __APPLE__
420
421
  // Can't use _cupsStrAlloc since it causes a loop with debug logging enabled
422
0
  cg->userconfig = strdup(temp);
423
0
#endif /* _WIN32 */
424
425
0
  return (cg);
426
0
}
427
428
429
/*
430
 * 'cups_globals_free()' - Free global data.
431
 */
432
433
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
434
static void
435
cups_globals_free(_cups_globals_t *cg)  /* I - Pointer to global data */
436
0
{
437
0
  _cups_buffer_t  *buffer,  /* Current read/write buffer */
438
0
      *next;    /* Next buffer */
439
440
441
0
  if (cg->last_status_message)
442
0
    _cupsStrFree(cg->last_status_message);
443
444
0
  for (buffer = cg->cups_buffers; buffer; buffer = next)
445
0
  {
446
0
    next = buffer->next;
447
0
    free(buffer);
448
0
  }
449
450
0
  cupsArrayDelete(cg->leg_size_lut);
451
0
  cupsArrayDelete(cg->ppd_size_lut);
452
0
  cupsArrayDelete(cg->pwg_size_lut);
453
454
0
  httpClose(cg->http);
455
456
0
  _httpFreeCredentials(cg->credentials);
457
458
0
  cupsFileClose(cg->stdio_files[0]);
459
0
  cupsFileClose(cg->stdio_files[1]);
460
0
  cupsFileClose(cg->stdio_files[2]);
461
462
0
  cupsArrayDelete(cg->browse_domains);
463
0
  cupsArrayDelete(cg->filter_location_array);
464
0
  if (cg->filter_location_regex)
465
0
  {
466
0
    regfree(cg->filter_location_regex);
467
0
    free(cg->filter_location_regex);
468
0
  }
469
470
0
  cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings);
471
472
0
  free(cg->userconfig);
473
0
  free(cg->raster_error.start);
474
0
  free(cg);
475
0
}
476
#endif /* HAVE_PTHREAD_H || _WIN32 */
477
478
479
#ifdef HAVE_PTHREAD_H
480
/*
481
 * 'cups_globals_init()' - Initialize environment variables.
482
 */
483
484
static void
485
cups_globals_init(void)
486
0
{
487
 /*
488
  * Register the global data for this thread...
489
  */
490
491
0
  pthread_key_create(&cups_globals_key, (void (*)(void *))cups_globals_free);
492
0
}
493
#endif /* HAVE_PTHREAD_H */