• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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