1 /*
2 * Printer class routines for the CUPS scheduler.
3 *
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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 "cupsd.h"
15
16
17 /*
18 * 'cupsdAddClass()' - Add a class to the system.
19 */
20
21 cupsd_printer_t * /* O - New class */
cupsdAddClass(const char * name)22 cupsdAddClass(const char *name) /* I - Name of class */
23 {
24 cupsd_printer_t *c; /* New class */
25 char uri[1024]; /* Class URI */
26
27
28 /*
29 * Add the printer and set the type to "class"...
30 */
31
32 if ((c = cupsdAddPrinter(name)) != NULL)
33 {
34 /*
35 * Change from a printer to a class...
36 */
37
38 c->type = CUPS_PRINTER_CLASS;
39
40 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
41 ServerName, RemotePort, "/classes/%s", name);
42 cupsdSetString(&c->uri, uri);
43
44 cupsdSetString(&c->error_policy, "retry-current-job");
45 }
46
47 return (c);
48 }
49
50
51 /*
52 * 'cupsdAddPrinterToClass()' - Add a printer to a class...
53 */
54
55 void
cupsdAddPrinterToClass(cupsd_printer_t * c,cupsd_printer_t * p)56 cupsdAddPrinterToClass(
57 cupsd_printer_t *c, /* I - Class to add to */
58 cupsd_printer_t *p) /* I - Printer to add */
59 {
60 int i; /* Looping var */
61 cupsd_printer_t **temp; /* Pointer to printer array */
62
63
64 /*
65 * See if this printer is already a member of the class...
66 */
67
68 for (i = 0; i < c->num_printers; i ++)
69 if (c->printers[i] == p)
70 return;
71
72 /*
73 * Allocate memory as needed...
74 */
75
76 if (c->num_printers == 0)
77 temp = malloc(sizeof(cupsd_printer_t *));
78 else
79 temp = realloc(c->printers, sizeof(cupsd_printer_t *) * (size_t)(c->num_printers + 1));
80
81 if (temp == NULL)
82 {
83 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add printer %s to class %s!",
84 p->name, c->name);
85 return;
86 }
87
88 /*
89 * Add the printer to the end of the array and update the number of printers.
90 */
91
92 c->printers = temp;
93 temp += c->num_printers;
94 c->num_printers ++;
95
96 *temp = p;
97 }
98
99
100 /*
101 * 'cupsdDeletePrinterFromClass()' - Delete a printer from a class.
102 */
103
104 int /* O - 1 if class changed, 0 otherwise */
cupsdDeletePrinterFromClass(cupsd_printer_t * c,cupsd_printer_t * p)105 cupsdDeletePrinterFromClass(
106 cupsd_printer_t *c, /* I - Class to delete from */
107 cupsd_printer_t *p) /* I - Printer to delete */
108 {
109 int i; /* Looping var */
110
111
112 /*
113 * See if the printer is in the class...
114 */
115
116 for (i = 0; i < c->num_printers; i ++)
117 if (p == c->printers[i])
118 break;
119
120 /*
121 * If it is, remove it from the list...
122 */
123
124 if (i < c->num_printers)
125 {
126 /*
127 * Yes, remove the printer...
128 */
129
130 c->num_printers --;
131 if (i < c->num_printers)
132 memmove(c->printers + i, c->printers + i + 1,
133 (size_t)(c->num_printers - i) * sizeof(cupsd_printer_t *));
134 }
135 else
136 return (0);
137
138 /*
139 * Update the IPP attributes (have to do this for member-names)...
140 */
141
142 cupsdSetPrinterAttrs(c);
143
144 return (1);
145 }
146
147
148 /*
149 * 'cupsdDeletePrinterFromClasses()' - Delete a printer from all classes.
150 */
151
152 int /* O - 1 if class changed, 0 otherwise */
cupsdDeletePrinterFromClasses(cupsd_printer_t * p)153 cupsdDeletePrinterFromClasses(
154 cupsd_printer_t *p) /* I - Printer to delete */
155 {
156 int changed = 0; /* Any class changed? */
157 cupsd_printer_t *c; /* Pointer to current class */
158
159
160 /*
161 * Loop through the printer/class list and remove the printer
162 * from each class listed...
163 */
164
165 for (c = (cupsd_printer_t *)cupsArrayFirst(Printers);
166 c;
167 c = (cupsd_printer_t *)cupsArrayNext(Printers))
168 if (c->type & CUPS_PRINTER_CLASS)
169 changed |= cupsdDeletePrinterFromClass(c, p);
170
171 return (changed);
172 }
173
174
175 /*
176 * 'cupsdFindAvailablePrinter()' - Find an available printer in a class.
177 */
178
179 cupsd_printer_t * /* O - Available printer or NULL */
cupsdFindAvailablePrinter(const char * name)180 cupsdFindAvailablePrinter(
181 const char *name) /* I - Class to check */
182 {
183 int i; /* Looping var */
184 cupsd_printer_t *c; /* Printer class */
185
186
187 /*
188 * Find the class...
189 */
190
191 if ((c = cupsdFindClass(name)) == NULL)
192 {
193 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to find class \"%s\"!", name);
194 return (NULL);
195 }
196
197 if (c->num_printers == 0)
198 return (NULL);
199
200 /*
201 * Make sure that the last printer is also a valid index into the printer
202 * array. If not, reset the last printer to 0...
203 */
204
205 if (c->last_printer >= c->num_printers)
206 c->last_printer = 0;
207
208 /*
209 * Loop through the printers in the class and return the first idle
210 * printer... We keep track of the last printer that we used so that
211 * a "round robin" type of scheduling is realized (otherwise the first
212 * server might be saturated with print jobs...)
213 *
214 * Thanks to Joel Fredrikson for helping us get this right!
215 */
216
217 for (i = c->last_printer + 1; ; i ++)
218 {
219 if (i >= c->num_printers)
220 i = 0;
221
222 if (c->printers[i]->accepting &&
223 (c->printers[i]->state == IPP_PRINTER_IDLE ||
224 ((c->printers[i]->type & CUPS_PRINTER_REMOTE) && !c->printers[i]->job)))
225 {
226 c->last_printer = i;
227 return (c->printers[i]);
228 }
229
230 if (i == c->last_printer)
231 break;
232 }
233
234 return (NULL);
235 }
236
237
238 /*
239 * 'cupsdFindClass()' - Find the named class.
240 */
241
242 cupsd_printer_t * /* O - Matching class or NULL */
cupsdFindClass(const char * name)243 cupsdFindClass(const char *name) /* I - Name of class */
244 {
245 cupsd_printer_t *c; /* Current class/printer */
246
247
248 if ((c = cupsdFindDest(name)) != NULL && (c->type & CUPS_PRINTER_CLASS))
249 return (c);
250 else
251 return (NULL);
252 }
253
254
255 /*
256 * 'cupsdLoadAllClasses()' - Load classes from the classes.conf file.
257 */
258
259 void
cupsdLoadAllClasses(void)260 cupsdLoadAllClasses(void)
261 {
262 int i; /* Looping var */
263 cups_file_t *fp; /* classes.conf file */
264 int linenum; /* Current line number */
265 char line[4096], /* Line from file */
266 *value, /* Pointer to value */
267 *valueptr; /* Pointer into value */
268 cupsd_printer_t *p, /* Current printer class */
269 *temp; /* Temporary pointer to printer */
270
271
272 /*
273 * Open the classes.conf file...
274 */
275
276 snprintf(line, sizeof(line), "%s/classes.conf", ServerRoot);
277 if ((fp = cupsdOpenConfFile(line)) == NULL)
278 return;
279
280 /*
281 * Read class configurations until we hit EOF...
282 */
283
284 linenum = 0;
285 p = NULL;
286
287 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
288 {
289 /*
290 * Decode the directive...
291 */
292
293 if (!_cups_strcasecmp(line, "<Class") ||
294 !_cups_strcasecmp(line, "<DefaultClass"))
295 {
296 /*
297 * <Class name> or <DefaultClass name>
298 */
299
300 if (p == NULL && value)
301 {
302 cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value);
303
304 /*
305 * Since prior classes may have implicitly defined this class,
306 * see if it already exists...
307 */
308
309 if ((p = cupsdFindDest(value)) != NULL)
310 {
311 p->type = CUPS_PRINTER_CLASS;
312 cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName,
313 LocalPort, value);
314 cupsdSetString(&p->error_policy, "retry-job");
315 }
316 else
317 p = cupsdAddClass(value);
318
319 p->accepting = 1;
320 p->state = IPP_PRINTER_IDLE;
321
322 if (!_cups_strcasecmp(line, "<DefaultClass"))
323 DefaultPrinter = p;
324 }
325 else
326 cupsdLogMessage(CUPSD_LOG_ERROR,
327 "Syntax error on line %d of classes.conf.", linenum);
328 }
329 else if (!_cups_strcasecmp(line, "</Class>") || !_cups_strcasecmp(line, "</DefaultClass>"))
330 {
331 if (p != NULL)
332 {
333 cupsdSetPrinterAttrs(p);
334 p = NULL;
335 }
336 else
337 cupsdLogMessage(CUPSD_LOG_ERROR,
338 "Syntax error on line %d of classes.conf.", linenum);
339 }
340 else if (!p)
341 {
342 cupsdLogMessage(CUPSD_LOG_ERROR,
343 "Syntax error on line %d of classes.conf.", linenum);
344 }
345 else if (!_cups_strcasecmp(line, "PrinterId"))
346 {
347 if (value && (i = atoi(value)) > 0)
348 p->printer_id = i;
349 else
350 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad PrinterId on line %d of classes.conf.", linenum);
351 }
352 else if (!_cups_strcasecmp(line, "UUID"))
353 {
354 if (value && !strncmp(value, "urn:uuid:", 9))
355 cupsdSetString(&(p->uuid), value);
356 else
357 cupsdLogMessage(CUPSD_LOG_ERROR,
358 "Bad UUID on line %d of classes.conf.", linenum);
359 }
360 else if (!_cups_strcasecmp(line, "AuthInfoRequired"))
361 {
362 if (!cupsdSetAuthInfoRequired(p, value, NULL))
363 cupsdLogMessage(CUPSD_LOG_ERROR,
364 "Bad AuthInfoRequired on line %d of classes.conf.",
365 linenum);
366 }
367 else if (!_cups_strcasecmp(line, "Info"))
368 {
369 if (value)
370 cupsdSetString(&p->info, value);
371 }
372 else if (!_cups_strcasecmp(line, "Location"))
373 {
374 if (value)
375 cupsdSetString(&p->location, value);
376 }
377 else if (!_cups_strcasecmp(line, "Option") && value)
378 {
379 /*
380 * Option name value
381 */
382
383 for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
384
385 if (!*valueptr)
386 cupsdLogMessage(CUPSD_LOG_ERROR,
387 "Syntax error on line %d of classes.conf.", linenum);
388 else
389 {
390 for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
391
392 p->num_options = cupsAddOption(value, valueptr, p->num_options,
393 &(p->options));
394 }
395 }
396 else if (!_cups_strcasecmp(line, "Printer"))
397 {
398 if (!value)
399 {
400 cupsdLogMessage(CUPSD_LOG_ERROR,
401 "Syntax error on line %d of classes.conf.", linenum);
402 continue;
403 }
404 else if ((temp = cupsdFindPrinter(value)) == NULL)
405 {
406 cupsdLogMessage(CUPSD_LOG_WARN,
407 "Unknown printer %s on line %d of classes.conf.",
408 value, linenum);
409
410 /*
411 * Add the missing remote printer...
412 */
413
414 if ((temp = cupsdAddPrinter(value)) != NULL)
415 {
416 cupsdSetString(&temp->make_model, "Remote Printer on unknown");
417
418 temp->state = IPP_PRINTER_STOPPED;
419 temp->type |= CUPS_PRINTER_REMOTE;
420
421 cupsdSetString(&temp->location, "Location Unknown");
422 cupsdSetString(&temp->info, "No Information Available");
423 temp->hostname[0] = '\0';
424
425 cupsdSetPrinterAttrs(temp);
426 }
427 }
428
429 if (temp)
430 cupsdAddPrinterToClass(p, temp);
431 }
432 else if (!_cups_strcasecmp(line, "State"))
433 {
434 /*
435 * Set the initial queue state...
436 */
437
438 if (!_cups_strcasecmp(value, "idle"))
439 p->state = IPP_PRINTER_IDLE;
440 else if (!_cups_strcasecmp(value, "stopped"))
441 {
442 p->state = IPP_PRINTER_STOPPED;
443
444 for (i = 0 ; i < p->num_reasons; i ++)
445 if (!strcmp("paused", p->reasons[i]))
446 break;
447
448 if (i >= p->num_reasons &&
449 p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
450 {
451 p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
452 p->num_reasons ++;
453 }
454 }
455 else
456 cupsdLogMessage(CUPSD_LOG_ERROR,
457 "Syntax error on line %d of classes.conf.",
458 linenum);
459 }
460 else if (!_cups_strcasecmp(line, "StateMessage"))
461 {
462 /*
463 * Set the initial queue state message...
464 */
465
466 if (value)
467 strlcpy(p->state_message, value, sizeof(p->state_message));
468 }
469 else if (!_cups_strcasecmp(line, "StateTime"))
470 {
471 /*
472 * Set the state time...
473 */
474
475 if (value)
476 p->state_time = atoi(value);
477 }
478 else if (!_cups_strcasecmp(line, "Accepting"))
479 {
480 /*
481 * Set the initial accepting state...
482 */
483
484 if (value &&
485 (!_cups_strcasecmp(value, "yes") ||
486 !_cups_strcasecmp(value, "on") ||
487 !_cups_strcasecmp(value, "true")))
488 p->accepting = 1;
489 else if (value &&
490 (!_cups_strcasecmp(value, "no") ||
491 !_cups_strcasecmp(value, "off") ||
492 !_cups_strcasecmp(value, "false")))
493 p->accepting = 0;
494 else
495 cupsdLogMessage(CUPSD_LOG_ERROR,
496 "Syntax error on line %d of classes.conf.",
497 linenum);
498 }
499 else if (!_cups_strcasecmp(line, "Shared"))
500 {
501 /*
502 * Set the initial shared state...
503 */
504
505 if (value &&
506 (!_cups_strcasecmp(value, "yes") ||
507 !_cups_strcasecmp(value, "on") ||
508 !_cups_strcasecmp(value, "true")))
509 p->shared = 1;
510 else if (value &&
511 (!_cups_strcasecmp(value, "no") ||
512 !_cups_strcasecmp(value, "off") ||
513 !_cups_strcasecmp(value, "false")))
514 p->shared = 0;
515 else
516 cupsdLogMessage(CUPSD_LOG_ERROR,
517 "Syntax error on line %d of classes.conf.",
518 linenum);
519 }
520 else if (!_cups_strcasecmp(line, "JobSheets"))
521 {
522 /*
523 * Set the initial job sheets...
524 */
525
526 if (value)
527 {
528 for (valueptr = value;
529 *valueptr && !isspace(*valueptr & 255);
530 valueptr ++);
531
532 if (*valueptr)
533 *valueptr++ = '\0';
534
535 cupsdSetString(&p->job_sheets[0], value);
536
537 while (isspace(*valueptr & 255))
538 valueptr ++;
539
540 if (*valueptr)
541 {
542 for (value = valueptr;
543 *valueptr && !isspace(*valueptr & 255);
544 valueptr ++);
545
546 if (*valueptr)
547 *valueptr = '\0';
548
549 cupsdSetString(&p->job_sheets[1], value);
550 }
551 }
552 else
553 cupsdLogMessage(CUPSD_LOG_ERROR,
554 "Syntax error on line %d of classes.conf.", linenum);
555 }
556 else if (!_cups_strcasecmp(line, "AllowUser"))
557 {
558 if (value)
559 {
560 p->deny_users = 0;
561 cupsdAddString(&(p->users), value);
562 }
563 else
564 cupsdLogMessage(CUPSD_LOG_ERROR,
565 "Syntax error on line %d of classes.conf.", linenum);
566 }
567 else if (!_cups_strcasecmp(line, "DenyUser"))
568 {
569 if (value)
570 {
571 p->deny_users = 1;
572 cupsdAddString(&(p->users), value);
573 }
574 else
575 cupsdLogMessage(CUPSD_LOG_ERROR,
576 "Syntax error on line %d of classes.conf.", linenum);
577 }
578 else if (!_cups_strcasecmp(line, "QuotaPeriod"))
579 {
580 if (value)
581 p->quota_period = atoi(value);
582 else
583 cupsdLogMessage(CUPSD_LOG_ERROR,
584 "Syntax error on line %d of classes.conf.", linenum);
585 }
586 else if (!_cups_strcasecmp(line, "PageLimit"))
587 {
588 if (value)
589 p->page_limit = atoi(value);
590 else
591 cupsdLogMessage(CUPSD_LOG_ERROR,
592 "Syntax error on line %d of classes.conf.", linenum);
593 }
594 else if (!_cups_strcasecmp(line, "KLimit"))
595 {
596 if (value)
597 p->k_limit = atoi(value);
598 else
599 cupsdLogMessage(CUPSD_LOG_ERROR,
600 "Syntax error on line %d of classes.conf.", linenum);
601 }
602 else if (!_cups_strcasecmp(line, "OpPolicy"))
603 {
604 if (value)
605 {
606 cupsd_policy_t *pol; /* Policy */
607
608
609 if ((pol = cupsdFindPolicy(value)) != NULL)
610 {
611 cupsdSetString(&p->op_policy, value);
612 p->op_policy_ptr = pol;
613 }
614 else
615 cupsdLogMessage(CUPSD_LOG_ERROR,
616 "Bad policy \"%s\" on line %d of classes.conf",
617 value, linenum);
618 }
619 else
620 cupsdLogMessage(CUPSD_LOG_ERROR,
621 "Syntax error on line %d of classes.conf.", linenum);
622 }
623 else if (!_cups_strcasecmp(line, "ErrorPolicy"))
624 {
625 if (value)
626 {
627 if (strcmp(value, "retry-current-job") && strcmp(value, "retry-job"))
628 cupsdLogMessage(CUPSD_LOG_WARN,
629 "ErrorPolicy %s ignored on line %d of classes.conf",
630 value, linenum);
631 }
632 else
633 cupsdLogMessage(CUPSD_LOG_ERROR,
634 "Syntax error on line %d of classes.conf.", linenum);
635 }
636 else
637 {
638 /*
639 * Something else we don't understand...
640 */
641
642 cupsdLogMessage(CUPSD_LOG_ERROR,
643 "Unknown configuration directive %s on line %d of classes.conf.",
644 line, linenum);
645 }
646 }
647
648 cupsFileClose(fp);
649 }
650
651
652 /*
653 * 'cupsdSaveAllClasses()' - Save classes to the classes.conf file.
654 */
655
656 void
cupsdSaveAllClasses(void)657 cupsdSaveAllClasses(void)
658 {
659 cups_file_t *fp; /* classes.conf file */
660 char filename[1024], /* classes.conf filename */
661 temp[1024], /* Temporary string */
662 value[2048], /* Value string */
663 *name; /* Current user name */
664 cupsd_printer_t *pclass; /* Current printer class */
665 int i; /* Looping var */
666 time_t curtime; /* Current time */
667 struct tm curdate; /* Current date */
668 cups_option_t *option; /* Current option */
669
670
671 /*
672 * Create the classes.conf file...
673 */
674
675 snprintf(filename, sizeof(filename), "%s/classes.conf", ServerRoot);
676
677 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
678 return;
679
680 cupsdLogMessage(CUPSD_LOG_INFO, "Saving classes.conf...");
681
682 /*
683 * Write a small header to the file...
684 */
685
686 time(&curtime);
687 localtime_r(&curtime, &curdate);
688 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", &curdate);
689
690 cupsFilePuts(fp, "# Class configuration file for " CUPS_SVERSION "\n");
691 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
692 cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
693
694 /*
695 * Write each local class known to the system...
696 */
697
698 for (pclass = (cupsd_printer_t *)cupsArrayFirst(Printers);
699 pclass;
700 pclass = (cupsd_printer_t *)cupsArrayNext(Printers))
701 {
702 /*
703 * Skip remote destinations and regular printers...
704 */
705
706 if ((pclass->type & CUPS_PRINTER_REMOTE) ||
707 !(pclass->type & CUPS_PRINTER_CLASS))
708 continue;
709
710 /*
711 * Write printers as needed...
712 */
713
714 if (pclass == DefaultPrinter)
715 cupsFilePrintf(fp, "<DefaultClass %s>\n", pclass->name);
716 else
717 cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
718
719 if (pclass->printer_id)
720 cupsFilePrintf(fp, "PrinterId %d\n", pclass->printer_id);
721
722 cupsFilePrintf(fp, "UUID %s\n", pclass->uuid);
723
724 if (pclass->num_auth_info_required > 0)
725 {
726 switch (pclass->num_auth_info_required)
727 {
728 case 1 :
729 strlcpy(value, pclass->auth_info_required[0], sizeof(value));
730 break;
731
732 case 2 :
733 snprintf(value, sizeof(value), "%s,%s",
734 pclass->auth_info_required[0],
735 pclass->auth_info_required[1]);
736 break;
737
738 case 3 :
739 default :
740 snprintf(value, sizeof(value), "%s,%s,%s",
741 pclass->auth_info_required[0],
742 pclass->auth_info_required[1],
743 pclass->auth_info_required[2]);
744 break;
745 }
746
747 cupsFilePutConf(fp, "AuthInfoRequired", value);
748 }
749
750 if (pclass->info)
751 cupsFilePutConf(fp, "Info", pclass->info);
752
753 if (pclass->location)
754 cupsFilePutConf(fp, "Location", pclass->location);
755
756 if (pclass->state == IPP_PRINTER_STOPPED)
757 cupsFilePuts(fp, "State Stopped\n");
758 else
759 cupsFilePuts(fp, "State Idle\n");
760
761 cupsFilePrintf(fp, "StateTime %d\n", (int)pclass->state_time);
762
763 if (pclass->accepting)
764 cupsFilePuts(fp, "Accepting Yes\n");
765 else
766 cupsFilePuts(fp, "Accepting No\n");
767
768 if (pclass->shared)
769 cupsFilePuts(fp, "Shared Yes\n");
770 else
771 cupsFilePuts(fp, "Shared No\n");
772
773 snprintf(value, sizeof(value), "%s %s", pclass->job_sheets[0],
774 pclass->job_sheets[1]);
775 cupsFilePutConf(fp, "JobSheets", value);
776
777 for (i = 0; i < pclass->num_printers; i ++)
778 cupsFilePrintf(fp, "Printer %s\n", pclass->printers[i]->name);
779
780 cupsFilePrintf(fp, "QuotaPeriod %d\n", pclass->quota_period);
781 cupsFilePrintf(fp, "PageLimit %d\n", pclass->page_limit);
782 cupsFilePrintf(fp, "KLimit %d\n", pclass->k_limit);
783
784 for (name = (char *)cupsArrayFirst(pclass->users);
785 name;
786 name = (char *)cupsArrayNext(pclass->users))
787 cupsFilePutConf(fp, pclass->deny_users ? "DenyUser" : "AllowUser", name);
788
789 if (pclass->op_policy)
790 cupsFilePutConf(fp, "OpPolicy", pclass->op_policy);
791 if (pclass->error_policy)
792 cupsFilePutConf(fp, "ErrorPolicy", pclass->error_policy);
793
794 for (i = pclass->num_options, option = pclass->options;
795 i > 0;
796 i --, option ++)
797 {
798 snprintf(value, sizeof(value), "%s %s", option->name, option->value);
799 cupsFilePutConf(fp, "Option", value);
800 }
801
802 if (pclass == DefaultPrinter)
803 cupsFilePuts(fp, "</DefaultClass>\n");
804 else
805 cupsFilePuts(fp, "</Class>\n");
806 }
807
808 cupsdCloseCreatedConfFile(fp, filename);
809 }
810