Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/scheduler/cups-deviced.c
1090 views
1
/*
2
* Device scanning mini-daemon for CUPS.
3
*
4
* Copyright © 2007-2018 by Apple Inc.
5
* Copyright © 1997-2006 by Easy Software Products.
6
*
7
* Licensed under Apache License v2.0. See the file "LICENSE" for more
8
* information.
9
*/
10
11
/*
12
* Include necessary headers...
13
*/
14
15
#include "util.h"
16
#include <cups/array.h>
17
#include <cups/dir.h>
18
#include <fcntl.h>
19
#include <sys/wait.h>
20
#include <poll.h>
21
22
23
/*
24
* Constants...
25
*/
26
27
#define MAX_BACKENDS 200 /* Maximum number of backends we'll run */
28
29
30
/*
31
* Backend information...
32
*/
33
34
typedef struct
35
{
36
char *name; /* Name of backend */
37
int pid, /* Process ID */
38
status; /* Exit status */
39
cups_file_t *pipe; /* Pipe from backend stdout */
40
int count; /* Number of devices found */
41
} cupsd_backend_t;
42
43
44
/*
45
* Device information structure...
46
*/
47
48
typedef struct
49
{
50
char device_class[128], /* Device class */
51
device_info[128], /* Device info/description */
52
device_uri[1024]; /* Device URI */
53
} cupsd_device_t;
54
55
56
/*
57
* Local globals...
58
*/
59
60
static int num_backends = 0,
61
/* Total backends */
62
active_backends = 0;
63
/* Active backends */
64
static cupsd_backend_t backends[MAX_BACKENDS];
65
/* Array of backends */
66
static struct pollfd backend_fds[MAX_BACKENDS];
67
/* Array for poll() */
68
static cups_array_t *devices; /* Array of devices */
69
static uid_t normal_user; /* Normal user ID */
70
static int device_limit; /* Maximum number of devices */
71
static int send_class, /* Send device-class attribute? */
72
send_info, /* Send device-info attribute? */
73
send_make_and_model,
74
/* Send device-make-and-model attribute? */
75
send_uri, /* Send device-uri attribute? */
76
send_id, /* Send device-id attribute? */
77
send_location; /* Send device-location attribute? */
78
static int dead_children = 0;
79
/* Dead children? */
80
81
82
/*
83
* Local functions...
84
*/
85
86
static int add_device(const char *device_class,
87
const char *device_make_and_model,
88
const char *device_info,
89
const char *device_uri,
90
const char *device_id,
91
const char *device_location);
92
static int compare_devices(cupsd_device_t *p0,
93
cupsd_device_t *p1);
94
static double get_current_time(void);
95
static int get_device(cupsd_backend_t *backend);
96
static void process_children(void);
97
static void sigchld_handler(int sig);
98
static int start_backend(const char *backend, int root);
99
100
101
/*
102
* 'main()' - Scan for devices and return an IPP response.
103
*
104
* Usage:
105
*
106
* cups-deviced request_id limit options
107
*/
108
109
int /* O - Exit code */
110
main(int argc, /* I - Number of command-line args */
111
char *argv[]) /* I - Command-line arguments */
112
{
113
int i; /* Looping var */
114
int request_id; /* Request ID */
115
int timeout; /* Timeout in seconds */
116
const char *server_bin; /* CUPS_SERVERBIN environment variable */
117
char filename[1024]; /* Backend directory filename */
118
cups_dir_t *dir; /* Directory pointer */
119
cups_dentry_t *dent; /* Directory entry */
120
double current_time, /* Current time */
121
end_time; /* Ending time */
122
int num_options; /* Number of options */
123
cups_option_t *options; /* Options */
124
cups_array_t *requested, /* requested-attributes values */
125
*exclude, /* exclude-schemes values */
126
*include; /* include-schemes values */
127
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
128
struct sigaction action; /* Actions for POSIX signals */
129
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
130
131
132
setbuf(stderr, NULL);
133
134
/*
135
* Check the command-line...
136
*/
137
138
if (argc != 6)
139
{
140
fputs("Usage: cups-deviced request-id limit timeout user-id options\n", stderr);
141
142
return (1);
143
}
144
145
request_id = atoi(argv[1]);
146
if (request_id < 1)
147
{
148
fprintf(stderr, "ERROR: [cups-deviced] Bad request ID %d!\n", request_id);
149
150
return (1);
151
}
152
153
device_limit = atoi(argv[2]);
154
if (device_limit < 0)
155
{
156
fprintf(stderr, "ERROR: [cups-deviced] Bad limit %d!\n", device_limit);
157
158
return (1);
159
}
160
161
timeout = atoi(argv[3]);
162
if (timeout < 1)
163
{
164
fprintf(stderr, "ERROR: [cups-deviced] Bad timeout %d!\n", timeout);
165
166
return (1);
167
}
168
169
normal_user = (uid_t)strtoul(argv[4], NULL, 10);
170
if (normal_user == 0)
171
{
172
fprintf(stderr, "ERROR: [cups-deviced] Bad user %u!\n", (unsigned)normal_user);
173
174
return (1);
175
}
176
177
num_options = cupsParseOptions(argv[5], 0, &options);
178
requested = cupsdCreateStringsArray(cupsGetOption("requested-attributes",
179
num_options, options));
180
exclude = cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
181
num_options, options));
182
include = cupsdCreateStringsArray(cupsGetOption("include-schemes",
183
num_options, options));
184
185
if (!requested || cupsArrayFind(requested, "all") != NULL)
186
{
187
send_class = send_info = send_make_and_model = send_uri = send_id =
188
send_location = 1;
189
}
190
else
191
{
192
send_class = cupsArrayFind(requested, "device-class") != NULL;
193
send_info = cupsArrayFind(requested, "device-info") != NULL;
194
send_make_and_model = cupsArrayFind(requested, "device-make-and-model") != NULL;
195
send_uri = cupsArrayFind(requested, "device-uri") != NULL;
196
send_id = cupsArrayFind(requested, "device-id") != NULL;
197
send_location = cupsArrayFind(requested, "device-location") != NULL;
198
}
199
200
/*
201
* Listen to child signals...
202
*/
203
204
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
205
sigset(SIGCHLD, sigchld_handler);
206
#elif defined(HAVE_SIGACTION)
207
memset(&action, 0, sizeof(action));
208
209
sigemptyset(&action.sa_mask);
210
sigaddset(&action.sa_mask, SIGCHLD);
211
action.sa_handler = sigchld_handler;
212
sigaction(SIGCHLD, &action, NULL);
213
#else
214
signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */
215
#endif /* HAVE_SIGSET */
216
217
/*
218
* Try opening the backend directory...
219
*/
220
221
if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
222
server_bin = CUPS_SERVERBIN;
223
224
snprintf(filename, sizeof(filename), "%s/backend", server_bin);
225
226
if ((dir = cupsDirOpen(filename)) == NULL)
227
{
228
fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory "
229
"\"%s\": %s", filename, strerror(errno));
230
231
return (1);
232
}
233
234
/*
235
* Setup the devices array...
236
*/
237
238
devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
239
240
/*
241
* Loop through all of the device backends...
242
*/
243
244
while ((dent = cupsDirRead(dir)) != NULL)
245
{
246
/*
247
* Skip entries that are not executable files...
248
*/
249
250
if (!S_ISREG(dent->fileinfo.st_mode) ||
251
!isalnum(dent->filename[0] & 255) ||
252
(dent->fileinfo.st_mode & (S_IRUSR | S_IXUSR)) != (S_IRUSR | S_IXUSR))
253
continue;
254
255
/*
256
* Skip excluded or not included backends...
257
*/
258
259
if (cupsArrayFind(exclude, dent->filename) ||
260
(include && !cupsArrayFind(include, dent->filename)))
261
continue;
262
263
/*
264
* Backends without permissions for normal users run as root,
265
* all others run as the unprivileged user...
266
*/
267
268
start_backend(dent->filename, !(dent->fileinfo.st_mode & (S_IWGRP | S_IWOTH | S_IXOTH)));
269
}
270
271
cupsDirClose(dir);
272
273
/*
274
* Collect devices...
275
*/
276
277
if (getenv("SOFTWARE"))
278
puts("Content-Type: application/ipp\n");
279
280
cupsdSendIPPHeader(IPP_OK, request_id);
281
cupsdSendIPPGroup(IPP_TAG_OPERATION);
282
cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
283
cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
284
285
end_time = get_current_time() + timeout;
286
287
while (active_backends > 0 && (current_time = get_current_time()) < end_time)
288
{
289
/*
290
* Collect the output from the backends...
291
*/
292
293
timeout = (int)(1000 * (end_time - current_time));
294
295
if (poll(backend_fds, (nfds_t)num_backends, timeout) > 0)
296
{
297
for (i = 0; i < num_backends; i ++)
298
if (backend_fds[i].revents && backends[i].pipe)
299
{
300
cups_file_t *bpipe = backends[i].pipe;
301
/* Copy of pipe for backend... */
302
303
do
304
{
305
if (get_device(backends + i))
306
{
307
backend_fds[i].fd = 0;
308
backend_fds[i].events = 0;
309
break;
310
}
311
}
312
while (_cupsFilePeekAhead(bpipe, '\n'));
313
}
314
}
315
316
/*
317
* Get exit status from children...
318
*/
319
320
if (dead_children)
321
process_children();
322
}
323
324
cupsdSendIPPTrailer();
325
326
/*
327
* Terminate any remaining backends and exit...
328
*/
329
330
if (active_backends > 0)
331
{
332
for (i = 0; i < num_backends; i ++)
333
if (backends[i].pid)
334
kill(backends[i].pid, SIGTERM);
335
}
336
337
return (0);
338
}
339
340
341
/*
342
* 'add_device()' - Add a new device to the list.
343
*/
344
345
static int /* O - 0 on success, -1 on error */
346
add_device(
347
const char *device_class, /* I - Device class */
348
const char *device_make_and_model, /* I - Device make and model */
349
const char *device_info, /* I - Device information */
350
const char *device_uri, /* I - Device URI */
351
const char *device_id, /* I - 1284 device ID */
352
const char *device_location) /* I - Physical location */
353
{
354
cupsd_device_t *device; /* New device */
355
356
357
/*
358
* Allocate memory for the device record...
359
*/
360
361
if ((device = calloc(1, sizeof(cupsd_device_t))) == NULL)
362
{
363
fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
364
stderr);
365
return (-1);
366
}
367
368
/*
369
* Copy the strings over...
370
*/
371
372
strlcpy(device->device_class, device_class, sizeof(device->device_class));
373
strlcpy(device->device_info, device_info, sizeof(device->device_info));
374
strlcpy(device->device_uri, device_uri, sizeof(device->device_uri));
375
376
/*
377
* Add the device to the array and return...
378
*/
379
380
if (cupsArrayFind(devices, device))
381
{
382
/*
383
* Avoid duplicates!
384
*/
385
386
free(device);
387
}
388
else
389
{
390
cupsArrayAdd(devices, device);
391
392
if (device_limit <= 0 || cupsArrayCount(devices) < device_limit)
393
{
394
/*
395
* Send device info...
396
*/
397
398
cupsdSendIPPGroup(IPP_TAG_PRINTER);
399
if (send_class)
400
cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class",
401
device_class);
402
if (send_info)
403
cupsdSendIPPString(IPP_TAG_TEXT, "device-info", device_info);
404
if (send_make_and_model)
405
cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
406
device_make_and_model);
407
if (send_uri)
408
cupsdSendIPPString(IPP_TAG_URI, "device-uri", device_uri);
409
if (send_id)
410
cupsdSendIPPString(IPP_TAG_TEXT, "device-id",
411
device_id ? device_id : "");
412
if (send_location)
413
cupsdSendIPPString(IPP_TAG_TEXT, "device-location",
414
device_location ? device_location : "");
415
416
fflush(stdout);
417
fputs("DEBUG: Flushed attributes...\n", stderr);
418
}
419
}
420
421
return (0);
422
}
423
424
425
/*
426
* 'compare_devices()' - Compare device names to eliminate duplicates.
427
*/
428
429
static int /* O - Result of comparison */
430
compare_devices(cupsd_device_t *d0, /* I - First device */
431
cupsd_device_t *d1) /* I - Second device */
432
{
433
int diff; /* Difference between strings */
434
435
436
/*
437
* Sort devices by device-info, device-class, and device-uri...
438
*/
439
440
if ((diff = cupsdCompareNames(d0->device_info, d1->device_info)) != 0)
441
return (diff);
442
else if ((diff = _cups_strcasecmp(d0->device_class, d1->device_class)) != 0)
443
return (diff);
444
else
445
return (_cups_strcasecmp(d0->device_uri, d1->device_uri));
446
}
447
448
449
/*
450
* 'get_current_time()' - Get the current time as a double value in seconds.
451
*/
452
453
static double /* O - Time in seconds */
454
get_current_time(void)
455
{
456
struct timeval curtime; /* Current time */
457
458
459
gettimeofday(&curtime, NULL);
460
461
return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
462
}
463
464
465
/*
466
* 'get_device()' - Get a device from a backend.
467
*/
468
469
static int /* O - 0 on success, -1 on error */
470
get_device(cupsd_backend_t *backend) /* I - Backend to read from */
471
{
472
char line[2048], /* Line from backend */
473
temp[2048], /* Copy of line */
474
*ptr, /* Pointer into line */
475
*dclass, /* Device class */
476
*uri, /* Device URI */
477
*make_model, /* Make and model */
478
*info, /* Device info */
479
*device_id, /* 1284 device ID */
480
*location; /* Physical location */
481
482
483
if (cupsFileGets(backend->pipe, line, sizeof(line)))
484
{
485
/*
486
* Each line is of the form:
487
*
488
* class URI "make model" "name" ["1284 device ID"] ["location"]
489
*/
490
491
strlcpy(temp, line, sizeof(temp));
492
493
/*
494
* device-class
495
*/
496
497
dclass = temp;
498
499
for (ptr = temp; *ptr; ptr ++)
500
if (isspace(*ptr & 255))
501
break;
502
503
while (isspace(*ptr & 255))
504
*ptr++ = '\0';
505
506
/*
507
* device-uri
508
*/
509
510
if (!*ptr)
511
goto error;
512
513
for (uri = ptr; *ptr; ptr ++)
514
if (isspace(*ptr & 255))
515
break;
516
517
while (isspace(*ptr & 255))
518
*ptr++ = '\0';
519
520
/*
521
* device-make-and-model
522
*/
523
524
if (*ptr != '\"')
525
goto error;
526
527
for (ptr ++, make_model = ptr; *ptr && *ptr != '\"'; ptr ++)
528
{
529
if (*ptr == '\\' && ptr[1])
530
_cups_strcpy(ptr, ptr + 1);
531
}
532
533
if (*ptr != '\"')
534
goto error;
535
536
for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
537
538
/*
539
* device-info
540
*/
541
542
if (*ptr != '\"')
543
goto error;
544
545
for (ptr ++, info = ptr; *ptr && *ptr != '\"'; ptr ++)
546
{
547
if (*ptr == '\\' && ptr[1])
548
_cups_strcpy(ptr, ptr + 1);
549
}
550
551
if (*ptr != '\"')
552
goto error;
553
554
for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
555
556
/*
557
* device-id
558
*/
559
560
if (*ptr == '\"')
561
{
562
for (ptr ++, device_id = ptr; *ptr && *ptr != '\"'; ptr ++)
563
{
564
if (*ptr == '\\' && ptr[1])
565
_cups_strcpy(ptr, ptr + 1);
566
}
567
568
if (*ptr != '\"')
569
goto error;
570
571
for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
572
573
/*
574
* device-location
575
*/
576
577
if (*ptr == '\"')
578
{
579
for (ptr ++, location = ptr; *ptr && *ptr != '\"'; ptr ++)
580
{
581
if (*ptr == '\\' && ptr[1])
582
_cups_strcpy(ptr, ptr + 1);
583
}
584
585
if (*ptr != '\"')
586
goto error;
587
588
*ptr = '\0';
589
}
590
else
591
location = NULL;
592
}
593
else
594
{
595
device_id = NULL;
596
location = NULL;
597
}
598
599
/*
600
* Add the device to the array of available devices...
601
*/
602
603
if (!add_device(dclass, make_model, info, uri, device_id, location))
604
fprintf(stderr, "DEBUG: [cups-deviced] Found device \"%s\"...\n", uri);
605
606
return (0);
607
}
608
609
/*
610
* End of file...
611
*/
612
613
cupsFileClose(backend->pipe);
614
backend->pipe = NULL;
615
616
return (-1);
617
618
/*
619
* Bad format; strip trailing newline and write an error message.
620
*/
621
622
error:
623
624
if (line[strlen(line) - 1] == '\n')
625
line[strlen(line) - 1] = '\0';
626
627
fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
628
backend->name, line);
629
return (0);
630
}
631
632
633
/*
634
* 'process_children()' - Process all dead children...
635
*/
636
637
static void
638
process_children(void)
639
{
640
int i; /* Looping var */
641
int status; /* Exit status of child */
642
int pid; /* Process ID of child */
643
cupsd_backend_t *backend; /* Current backend */
644
const char *name; /* Name of process */
645
646
647
/*
648
* Reset the dead_children flag...
649
*/
650
651
dead_children = 0;
652
653
/*
654
* Collect the exit status of some children...
655
*/
656
657
#ifdef HAVE_WAITPID
658
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
659
#elif defined(HAVE_WAIT3)
660
while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
661
#else
662
if ((pid = wait(&status)) > 0)
663
#endif /* HAVE_WAITPID */
664
{
665
if (status == SIGTERM)
666
status = 0;
667
668
for (i = num_backends, backend = backends; i > 0; i --, backend ++)
669
if (backend->pid == pid)
670
break;
671
672
if (i > 0)
673
{
674
name = backend->name;
675
backend->pid = 0;
676
backend->status = status;
677
678
active_backends --;
679
}
680
else
681
name = "Unknown";
682
683
if (status)
684
{
685
if (WIFEXITED(status))
686
fprintf(stderr,
687
"ERROR: [cups-deviced] PID %d (%s) stopped with status %d!\n",
688
pid, name, WEXITSTATUS(status));
689
else
690
fprintf(stderr,
691
"ERROR: [cups-deviced] PID %d (%s) crashed on signal %d!\n",
692
pid, name, WTERMSIG(status));
693
}
694
else
695
fprintf(stderr,
696
"DEBUG: [cups-deviced] PID %d (%s) exited with no errors.\n",
697
pid, name);
698
}
699
}
700
701
702
/*
703
* 'sigchld_handler()' - Handle 'child' signals from old processes.
704
*/
705
706
static void
707
sigchld_handler(int sig) /* I - Signal number */
708
{
709
(void)sig;
710
711
/*
712
* Flag that we have dead children...
713
*/
714
715
dead_children = 1;
716
717
/*
718
* Reset the signal handler as needed...
719
*/
720
721
#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
722
signal(SIGCLD, sigchld_handler);
723
#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
724
}
725
726
727
/*
728
* 'start_backend()' - Run a backend to gather the available devices.
729
*/
730
731
static int /* O - 0 on success, -1 on error */
732
start_backend(const char *name, /* I - Backend to run */
733
int root) /* I - Run as root? */
734
{
735
const char *server_bin; /* CUPS_SERVERBIN environment variable */
736
char program[1024]; /* Full path to backend */
737
cupsd_backend_t *backend; /* Current backend */
738
char *argv[2]; /* Command-line arguments */
739
740
741
if (num_backends >= MAX_BACKENDS)
742
{
743
fprintf(stderr, "ERROR: Too many backends (%d)!\n", num_backends);
744
return (-1);
745
}
746
747
if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
748
server_bin = CUPS_SERVERBIN;
749
750
snprintf(program, sizeof(program), "%s/backend/%s", server_bin, name);
751
752
if (_cupsFileCheck(program, _CUPS_FILE_CHECK_PROGRAM, !geteuid(),
753
_cupsFileCheckFilter, NULL))
754
return (-1);
755
756
backend = backends + num_backends;
757
758
argv[0] = (char *)name;
759
argv[1] = NULL;
760
761
if ((backend->pipe = cupsdPipeCommand(&(backend->pid), program, argv,
762
root ? 0 : normal_user)) == NULL)
763
{
764
fprintf(stderr, "ERROR: [cups-deviced] Unable to execute \"%s\" - %s\n",
765
program, strerror(errno));
766
return (-1);
767
}
768
769
/*
770
* Fill in the rest of the backend information...
771
*/
772
773
fprintf(stderr, "DEBUG: [cups-deviced] Started backend %s (PID %d)\n",
774
program, backend->pid);
775
776
backend_fds[num_backends].fd = cupsFileNumber(backend->pipe);
777
backend_fds[num_backends].events = POLLIN;
778
779
backend->name = strdup(name);
780
backend->status = 0;
781
backend->count = 0;
782
783
active_backends ++;
784
num_backends ++;
785
786
return (0);
787
}
788
789