1 /*
2 * Mini-daemon utility functions for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright 2007-2014 by Apple Inc.
6 * Copyright 1997-2005 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include "util.h"
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #ifdef __APPLE__
20 # include <libgen.h>
21 extern char **environ;
22 #endif /* __APPLE__ */
23
24
25 /*
26 * 'cupsdCompareNames()' - Compare two names.
27 *
28 * This function basically does a _cups_strcasecmp() of the two strings,
29 * but is also aware of numbers so that "a2" < "a100".
30 */
31
32 int /* O - Result of comparison */
cupsdCompareNames(const char * s,const char * t)33 cupsdCompareNames(const char *s, /* I - First string */
34 const char *t) /* I - Second string */
35 {
36 int diff, /* Difference between digits */
37 digits; /* Number of digits */
38
39
40 /*
41 * Loop through both names, returning only when a difference is
42 * seen. Also, compare whole numbers rather than just characters, too!
43 */
44
45 while (*s && *t)
46 {
47 if (isdigit(*s & 255) && isdigit(*t & 255))
48 {
49 /*
50 * Got a number; start by skipping leading 0's...
51 */
52
53 while (*s == '0')
54 s ++;
55 while (*t == '0')
56 t ++;
57
58 /*
59 * Skip equal digits...
60 */
61
62 while (isdigit(*s & 255) && *s == *t)
63 {
64 s ++;
65 t ++;
66 }
67
68 /*
69 * Bounce out if *s and *t aren't both digits...
70 */
71
72 if (isdigit(*s & 255) && !isdigit(*t & 255))
73 return (1);
74 else if (!isdigit(*s & 255) && isdigit(*t & 255))
75 return (-1);
76 else if (!isdigit(*s & 255) || !isdigit(*t & 255))
77 continue;
78
79 if (*s < *t)
80 diff = -1;
81 else
82 diff = 1;
83
84 /*
85 * Figure out how many more digits there are...
86 */
87
88 digits = 0;
89 s ++;
90 t ++;
91
92 while (isdigit(*s & 255))
93 {
94 digits ++;
95 s ++;
96 }
97
98 while (isdigit(*t & 255))
99 {
100 digits --;
101 t ++;
102 }
103
104 /*
105 * Return if the number or value of the digits is different...
106 */
107
108 if (digits < 0)
109 return (-1);
110 else if (digits > 0)
111 return (1);
112 else
113 return (diff);
114 }
115 else if (tolower(*s) < tolower(*t))
116 return (-1);
117 else if (tolower(*s) > tolower(*t))
118 return (1);
119 else
120 {
121 s ++;
122 t ++;
123 }
124 }
125
126 /*
127 * Return the results of the final comparison...
128 */
129
130 if (*s)
131 return (1);
132 else if (*t)
133 return (-1);
134 else
135 return (0);
136 }
137
138
139 /*
140 * 'cupsdCreateStringsArray()' - Create a CUPS array of strings.
141 */
142
143 cups_array_t * /* O - CUPS array */
cupsdCreateStringsArray(const char * s)144 cupsdCreateStringsArray(const char *s) /* I - Comma-delimited strings */
145 {
146 if (s && *s)
147 return (_cupsArrayNewStrings(s, ','));
148 else
149 return (NULL);
150 }
151
152
153 /*
154 * 'cupsdExec()' - Run a program with the correct environment.
155 *
156 * On macOS, we need to update the CFProcessPath environment variable that
157 * is passed in the environment so the child can access its bundled resources.
158 */
159
160 int /* O - exec() status */
cupsdExec(const char * command,char ** argv)161 cupsdExec(const char *command, /* I - Full path to program */
162 char **argv) /* I - Command-line arguments */
163 {
164 #ifdef __APPLE__
165 size_t i, j; /* Looping vars */
166 char *envp[500], /* Array of environment variables */
167 cfprocesspath[1024], /* CFProcessPath environment variable */
168 linkpath[1024]; /* Link path for symlinks... */
169 ssize_t linkbytes; /* Bytes for link path */
170
171
172 /*
173 * Some macOS programs are bundled and need the CFProcessPath environment
174 * variable defined. If the command is a symlink, resolve the link and point
175 * to the resolved location, otherwise, use the command path itself.
176 */
177
178 if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
179 {
180 /*
181 * Yes, this is a symlink to the actual program, nul-terminate and
182 * use it...
183 */
184
185 linkpath[linkbytes] = '\0';
186
187 if (linkpath[0] == '/')
188 snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s",
189 linkpath);
190 else
191 snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s/%s",
192 dirname((char *)command), linkpath);
193 }
194 else
195 snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s", command);
196
197 envp[0] = cfprocesspath;
198
199 /*
200 * Copy the rest of the environment except for any CFProcessPath that may
201 * already be there...
202 */
203
204 for (i = 1, j = 0;
205 environ[j] && i < (sizeof(envp) / sizeof(envp[0]) - 1);
206 j ++)
207 if (strncmp(environ[j], "CFProcessPath=", 14))
208 envp[i ++] = environ[j];
209
210 envp[i] = NULL;
211
212 /*
213 * Use execve() to run the program...
214 */
215
216 return (execve(command, argv, envp));
217
218 #else
219 /*
220 * On other operating systems, just call execv() to use the same environment
221 * variables as the parent...
222 */
223
224 return (execv(command, argv));
225 #endif /* __APPLE__ */
226 }
227
228
229 /*
230 * 'cupsdPipeCommand()' - Read output from a command.
231 */
232
233 cups_file_t * /* O - CUPS file or NULL on error */
cupsdPipeCommand(int * pid,const char * command,char ** argv,uid_t user)234 cupsdPipeCommand(int *pid, /* O - Process ID or 0 on error */
235 const char *command, /* I - Command to run */
236 char **argv, /* I - Arguments to pass to command */
237 uid_t user) /* I - User to run as or 0 for current */
238 {
239 int fd, /* Temporary file descriptor */
240 fds[2]; /* Pipe file descriptors */
241
242
243 /*
244 * First create the pipe...
245 */
246
247 if (pipe(fds))
248 {
249 *pid = 0;
250 return (NULL);
251 }
252
253 /*
254 * Set the "close on exec" flag on each end of the pipe...
255 */
256
257 if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
258 {
259 close(fds[0]);
260 close(fds[1]);
261
262 *pid = 0;
263
264 return (NULL);
265 }
266
267 if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
268 {
269 close(fds[0]);
270 close(fds[1]);
271
272 *pid = 0;
273
274 return (NULL);
275 }
276
277 /*
278 * Then run the command...
279 */
280
281 if ((*pid = fork()) < 0)
282 {
283 /*
284 * Unable to fork!
285 */
286
287 *pid = 0;
288 close(fds[0]);
289 close(fds[1]);
290
291 return (NULL);
292 }
293 else if (!*pid)
294 {
295 /*
296 * Child comes here...
297 */
298
299 if (!getuid() && user)
300 setuid(user); /* Run as restricted user */
301
302 if ((fd = open("/dev/null", O_RDONLY)) > 0)
303 {
304 dup2(fd, 0); /* </dev/null */
305 close(fd);
306 }
307
308 dup2(fds[1], 1); /* >pipe */
309 close(fds[1]);
310
311 cupsdExec(command, argv);
312 exit(errno);
313 }
314
315 /*
316 * Parent comes here, open the input side of the pipe...
317 */
318
319 close(fds[1]);
320
321 return (cupsFileOpenFd(fds[0], "r"));
322 }
323
324
325 /*
326 * 'cupsdSendIPPGroup()' - Send a group tag.
327 */
328
329 void
cupsdSendIPPGroup(ipp_tag_t group_tag)330 cupsdSendIPPGroup(ipp_tag_t group_tag) /* I - Group tag */
331 {
332 /*
333 * Send IPP group tag (1 byte)...
334 */
335
336 putchar(group_tag);
337 }
338
339
340 /*
341 * 'cupsdSendIPPHeader()' - Send the IPP response header.
342 */
343
344 void
cupsdSendIPPHeader(ipp_status_t status_code,int request_id)345 cupsdSendIPPHeader(
346 ipp_status_t status_code, /* I - Status code */
347 int request_id) /* I - Request ID */
348 {
349 /*
350 * Send IPP/1.1 response header: version number (2 bytes), status code
351 * (2 bytes), and request ID (4 bytes)...
352 *
353 * TODO: Add version number (IPP/2.x and IPP/1.0) support.
354 */
355
356 putchar(1);
357 putchar(1);
358
359 putchar(status_code >> 8);
360 putchar(status_code);
361
362 putchar(request_id >> 24);
363 putchar(request_id >> 16);
364 putchar(request_id >> 8);
365 putchar(request_id);
366 }
367
368
369 /*
370 * 'cupsdSendIPPInteger()' - Send an integer attribute.
371 */
372
373 void
cupsdSendIPPInteger(ipp_tag_t value_tag,const char * name,int value)374 cupsdSendIPPInteger(
375 ipp_tag_t value_tag, /* I - Value tag */
376 const char *name, /* I - Attribute name */
377 int value) /* I - Attribute value */
378 {
379 size_t len; /* Length of attribute name */
380
381
382 /*
383 * Send IPP integer value: value tag (1 byte), name length (2 bytes),
384 * name string (without nul), value length (2 bytes), and value (4 bytes)...
385 */
386
387 putchar(value_tag);
388
389 len = strlen(name);
390 putchar((int)(len >> 8));
391 putchar((int)len);
392
393 fputs(name, stdout);
394
395 putchar(0);
396 putchar(4);
397
398 putchar(value >> 24);
399 putchar(value >> 16);
400 putchar(value >> 8);
401 putchar(value);
402 }
403
404
405 /*
406 * 'cupsdSendIPPString()' - Send a string attribute.
407 */
408
409 void
cupsdSendIPPString(ipp_tag_t value_tag,const char * name,const char * value)410 cupsdSendIPPString(
411 ipp_tag_t value_tag, /* I - Value tag */
412 const char *name, /* I - Attribute name */
413 const char *value) /* I - Attribute value */
414 {
415 size_t len; /* Length of attribute name */
416
417
418 /*
419 * Send IPP string value: value tag (1 byte), name length (2 bytes),
420 * name string (without nul), value length (2 bytes), and value string
421 * (without nul)...
422 */
423
424 putchar(value_tag);
425
426 len = strlen(name);
427 putchar((int)(len >> 8));
428 putchar((int)len);
429
430 fputs(name, stdout);
431
432 len = strlen(value);
433 putchar((int)(len >> 8));
434 putchar((int)len);
435
436 fputs(value, stdout);
437 }
438
439
440 /*
441 * 'cupsdSendIPPTrailer()' - Send the end-of-message tag.
442 */
443
444 void
cupsdSendIPPTrailer(void)445 cupsdSendIPPTrailer(void)
446 {
447 putchar(IPP_TAG_END);
448 fflush(stdout);
449 }
450