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