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