]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/util.c
Merge changes from CUPS 1.5svn-r9352.
[thirdparty/cups.git] / scheduler / util.c
CommitLineData
ef416fc2 1/*
75bd9771 2 * "$Id: util.c 7621 2008-06-06 18:55:35Z mike $"
ef416fc2 3 *
10d09e33 4 * Mini-daemon utility functions for CUPS.
ef416fc2 5 *
10d09e33 6 * Copyright 2007-2010 by Apple Inc.
ef416fc2 7 * Copyright 1997-2005 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 14 *
15 * Contents:
16 *
ed6e7faf
MS
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.
ef416fc2 26 */
27
28/*
29 * Include necessary headers...
30 */
31
32#include "util.h"
c934a06c
MS
33#include <unistd.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#ifdef __APPLE__
37# include <libgen.h>
38extern char **environ;
39#endif /* __APPLE__ */
ef416fc2 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
49int /* O - Result of comparison */
50cupsdCompareNames(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
ed6e7faf
MS
156/*
157 * 'cupsdCreateStringsArray()' - Create a CUPS array of strings.
158 */
159
160cups_array_t * /* O - CUPS array */
161cupsdCreateStringsArray(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 */
10d09e33 166 char buffer[8192]; /* New string */
ed6e7faf
MS
167
168
169 if (!s)
170 return (NULL);
171
10d09e33
MS
172 if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL,
173 (cups_ahash_func_t)NULL, 0,
174 (cups_acopy_func_t)_cupsStrAlloc,
175 (cups_afree_func_t)_cupsStrFree)) != NULL)
ed6e7faf
MS
176 {
177 for (start = end = s; *end; start = end + 1)
178 {
179 /*
180 * Find the end of the current delimited string...
181 */
182
183 if ((end = strchr(start, ',')) == NULL)
10d09e33
MS
184 {
185 /*
186 * Last delimited string...
187 */
188
189 cupsArrayAdd(a, (char *)start);
190 break;
191 }
ed6e7faf
MS
192
193 /*
10d09e33 194 * Copy the string and add it to the array...
ed6e7faf
MS
195 */
196
10d09e33 197 if ((end - start + 1) > sizeof(buffer))
ed6e7faf
MS
198 break;
199
10d09e33
MS
200 memcpy(buffer, start, end - start);
201 buffer[end - start] = '\0';
202
203 cupsArrayAdd(a, buffer);
ed6e7faf
MS
204 }
205 }
206
207 return (a);
208}
209
210
c934a06c
MS
211/*
212 * 'cupsdExec()' - Run a program with the correct environment.
213 *
214 * On Mac OS X, we need to update the CFProcessPath environment variable that
215 * is passed in the environment so the child can access its bundled resources.
216 */
217
218int /* O - exec() status */
219cupsdExec(const char *command, /* I - Full path to program */
220 char **argv) /* I - Command-line arguments */
221{
222#ifdef __APPLE__
223 int i, j; /* Looping vars */
224 char *envp[500], /* Array of environment variables */
225 cfprocesspath[1024], /* CFProcessPath environment variable */
226 linkpath[1024]; /* Link path for symlinks... */
227 int linkbytes; /* Bytes for link path */
228
229
230 /*
231 * Some Mac OS X programs are bundled and need the CFProcessPath environment
232 * variable defined. If the command is a symlink, resolve the link and point
233 * to the resolved location, otherwise, use the command path itself.
234 */
235
236 if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
237 {
238 /*
239 * Yes, this is a symlink to the actual program, nul-terminate and
240 * use it...
241 */
242
243 linkpath[linkbytes] = '\0';
244
245 if (linkpath[0] == '/')
246 snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s",
247 linkpath);
248 else
249 snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s/%s",
250 dirname((char *)command), linkpath);
251 }
252 else
253 snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s", command);
254
255 envp[0] = cfprocesspath;
256
257 /*
258 * Copy the rest of the environment except for any CFProcessPath that may
259 * already be there...
260 */
261
262 for (i = 1, j = 0;
263 environ[j] && i < (int)(sizeof(envp) / sizeof(envp[0]) - 1);
264 j ++)
265 if (strncmp(environ[j], "CFProcessPath=", 14))
266 envp[i ++] = environ[j];
267
268 envp[i] = NULL;
269
270 /*
271 * Use execve() to run the program...
272 */
273
274 return (execve(command, argv, envp));
275
276#else
277 /*
278 * On other operating systems, just call execv() to use the same environment
279 * variables as the parent...
280 */
281
282 return (execv(command, argv));
283#endif /* __APPLE__ */
284}
285
286
287/*
288 * 'cupsdPipeCommand()' - Read output from a command.
289 */
290
291cups_file_t * /* O - CUPS file or NULL on error */
292cupsdPipeCommand(int *pid, /* O - Process ID or 0 on error */
293 const char *command, /* I - Command to run */
294 char **argv, /* I - Arguments to pass to command */
295 int user) /* I - User to run as or 0 for current */
296{
97c9a8d7
MS
297 int fd, /* Temporary file descriptor */
298 fds[2]; /* Pipe file descriptors */
c934a06c
MS
299
300
301 /*
302 * First create the pipe...
303 */
304
305 if (pipe(fds))
306 {
307 *pid = 0;
308 return (NULL);
309 }
310
311 /*
312 * Set the "close on exec" flag on each end of the pipe...
313 */
314
315 if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
316 {
317 close(fds[0]);
318 close(fds[1]);
319
320 *pid = 0;
321
322 return (NULL);
323 }
324
325 if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
326 {
327 close(fds[0]);
328 close(fds[1]);
329
330 *pid = 0;
331
332 return (NULL);
333 }
334
335 /*
336 * Then run the command...
337 */
338
339 if ((*pid = fork()) < 0)
340 {
341 /*
342 * Unable to fork!
343 */
344
345 *pid = 0;
346 close(fds[0]);
347 close(fds[1]);
348
349 return (NULL);
350 }
351 else if (!*pid)
352 {
353 /*
354 * Child comes here...
355 */
356
357 if (!getuid() && user)
358 setuid(user); /* Run as restricted user */
359
97c9a8d7
MS
360 if ((fd = open("/dev/null", O_RDONLY)) > 0)
361 {
362 dup2(fd, 0); /* </dev/null */
363 close(fd);
364 }
c934a06c 365
97c9a8d7
MS
366 dup2(fds[1], 1); /* >pipe */
367 close(fds[1]);
c934a06c
MS
368
369 cupsdExec(command, argv);
370 exit(errno);
371 }
372
373 /*
374 * Parent comes here, open the input side of the pipe...
375 */
376
377 close(fds[1]);
378
379 return (cupsFileOpenFd(fds[0], "r"));
380}
381
382
ef416fc2 383/*
384 * 'cupsdSendIPPGroup()' - Send a group tag.
385 */
386
387void
388cupsdSendIPPGroup(ipp_tag_t group_tag) /* I - Group tag */
389{
390 /*
391 * Send IPP group tag (1 byte)...
392 */
393
394 putchar(group_tag);
395}
396
397
398/*
399 * 'cupsdSendIPPHeader()' - Send the IPP response header.
400 */
401
402void
403cupsdSendIPPHeader(
404 ipp_status_t status_code, /* I - Status code */
405 int request_id) /* I - Request ID */
406{
407 /*
408 * Send IPP/1.1 response header: version number (2 bytes), status code
409 * (2 bytes), and request ID (4 bytes)...
94da7e34
MS
410 *
411 * TODO: Add version number (IPP/2.x and IPP/1.0) support.
ef416fc2 412 */
413
414 putchar(1);
415 putchar(1);
416
417 putchar(status_code >> 8);
418 putchar(status_code);
419
420 putchar(request_id >> 24);
421 putchar(request_id >> 16);
422 putchar(request_id >> 8);
423 putchar(request_id);
424}
425
426
427/*
428 * 'cupsdSendIPPInteger()' - Send an integer attribute.
429 */
430
431void
432cupsdSendIPPInteger(
433 ipp_tag_t value_tag, /* I - Value tag */
434 const char *name, /* I - Attribute name */
435 int value) /* I - Attribute value */
436{
437 size_t len; /* Length of attribute name */
438
439
440 /*
441 * Send IPP integer value: value tag (1 byte), name length (2 bytes),
3d8365b8 442 * name string (without nul), value length (2 bytes), and value (4 bytes)...
ef416fc2 443 */
444
445 putchar(value_tag);
446
447 len = strlen(name);
448 putchar(len >> 8);
449 putchar(len);
450
451 fputs(name, stdout);
452
3d8365b8 453 putchar(0);
454 putchar(4);
455
ef416fc2 456 putchar(value >> 24);
457 putchar(value >> 16);
458 putchar(value >> 8);
459 putchar(value);
460}
461
462
463/*
464 * 'cupsdSendIPPString()' - Send a string attribute.
465 */
466
467void
468cupsdSendIPPString(
469 ipp_tag_t value_tag, /* I - Value tag */
470 const char *name, /* I - Attribute name */
471 const char *value) /* I - Attribute value */
472{
473 size_t len; /* Length of attribute name */
474
475
476 /*
477 * Send IPP string value: value tag (1 byte), name length (2 bytes),
478 * name string (without nul), value length (2 bytes), and value string
479 * (without nul)...
480 */
481
482 putchar(value_tag);
483
484 len = strlen(name);
485 putchar(len >> 8);
486 putchar(len);
487
488 fputs(name, stdout);
489
490 len = strlen(value);
491 putchar(len >> 8);
492 putchar(len);
493
494 fputs(value, stdout);
495}
496
497
498/*
499 * 'cupsdSendIPPTrailer()' - Send the end-of-message tag.
500 */
501
502void
503cupsdSendIPPTrailer(void)
504{
505 putchar(IPP_TAG_END);
506 fflush(stdout);
507}
508
509
510/*
75bd9771 511 * End of "$Id: util.c 7621 2008-06-06 18:55:35Z mike $".
ef416fc2 512 */