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