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