Coverage Report

Created: 2025-07-14 06:27

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