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 */ |