Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/scheduler/classes.c
1090 views
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 */
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
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 */
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 */
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 */
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 */
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
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
657
cupsdSaveAllClasses(void)
658
{
659
cups_file_t *fp; /* classes.conf file */
660
char filename[1024], /* classes.conf filename */
661
value[2048], /* Value string */
662
*name; /* Current user name */
663
cupsd_printer_t *pclass; /* Current printer class */
664
int i; /* Looping var */
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
cupsFilePuts(fp, "# Class configuration file for " CUPS_SVERSION "\n");
684
cupsFilePrintf(fp, "# Written by cupsd\n");
685
cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
686
687
/*
688
* Write each local class known to the system...
689
*/
690
691
for (pclass = (cupsd_printer_t *)cupsArrayFirst(Printers);
692
pclass;
693
pclass = (cupsd_printer_t *)cupsArrayNext(Printers))
694
{
695
/*
696
* Skip remote destinations and regular printers...
697
*/
698
699
if ((pclass->type & CUPS_PRINTER_REMOTE) ||
700
!(pclass->type & CUPS_PRINTER_CLASS))
701
continue;
702
703
/*
704
* Write printers as needed...
705
*/
706
707
if (pclass == DefaultPrinter)
708
cupsFilePrintf(fp, "<DefaultClass %s>\n", pclass->name);
709
else
710
cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
711
712
if (pclass->printer_id)
713
cupsFilePrintf(fp, "PrinterId %d\n", pclass->printer_id);
714
715
cupsFilePrintf(fp, "UUID %s\n", pclass->uuid);
716
717
if (pclass->num_auth_info_required > 0)
718
{
719
switch (pclass->num_auth_info_required)
720
{
721
case 1 :
722
strlcpy(value, pclass->auth_info_required[0], sizeof(value));
723
break;
724
725
case 2 :
726
snprintf(value, sizeof(value), "%s,%s",
727
pclass->auth_info_required[0],
728
pclass->auth_info_required[1]);
729
break;
730
731
case 3 :
732
default :
733
snprintf(value, sizeof(value), "%s,%s,%s",
734
pclass->auth_info_required[0],
735
pclass->auth_info_required[1],
736
pclass->auth_info_required[2]);
737
break;
738
}
739
740
cupsFilePutConf(fp, "AuthInfoRequired", value);
741
}
742
743
if (pclass->info)
744
cupsFilePutConf(fp, "Info", pclass->info);
745
746
if (pclass->location)
747
cupsFilePutConf(fp, "Location", pclass->location);
748
749
if (pclass->state == IPP_PRINTER_STOPPED)
750
cupsFilePuts(fp, "State Stopped\n");
751
else
752
cupsFilePuts(fp, "State Idle\n");
753
754
cupsFilePrintf(fp, "StateTime %d\n", (int)pclass->state_time);
755
756
if (pclass->accepting)
757
cupsFilePuts(fp, "Accepting Yes\n");
758
else
759
cupsFilePuts(fp, "Accepting No\n");
760
761
if (pclass->shared)
762
cupsFilePuts(fp, "Shared Yes\n");
763
else
764
cupsFilePuts(fp, "Shared No\n");
765
766
snprintf(value, sizeof(value), "%s %s", pclass->job_sheets[0],
767
pclass->job_sheets[1]);
768
cupsFilePutConf(fp, "JobSheets", value);
769
770
for (i = 0; i < pclass->num_printers; i ++)
771
cupsFilePrintf(fp, "Printer %s\n", pclass->printers[i]->name);
772
773
cupsFilePrintf(fp, "QuotaPeriod %d\n", pclass->quota_period);
774
cupsFilePrintf(fp, "PageLimit %d\n", pclass->page_limit);
775
cupsFilePrintf(fp, "KLimit %d\n", pclass->k_limit);
776
777
for (name = (char *)cupsArrayFirst(pclass->users);
778
name;
779
name = (char *)cupsArrayNext(pclass->users))
780
cupsFilePutConf(fp, pclass->deny_users ? "DenyUser" : "AllowUser", name);
781
782
if (pclass->op_policy)
783
cupsFilePutConf(fp, "OpPolicy", pclass->op_policy);
784
if (pclass->error_policy)
785
cupsFilePutConf(fp, "ErrorPolicy", pclass->error_policy);
786
787
for (i = pclass->num_options, option = pclass->options;
788
i > 0;
789
i --, option ++)
790
{
791
snprintf(value, sizeof(value), "%s %s", option->name, option->value);
792
cupsFilePutConf(fp, "Option", value);
793
}
794
795
if (pclass == DefaultPrinter)
796
cupsFilePuts(fp, "</DefaultClass>\n");
797
else
798
cupsFilePuts(fp, "</Class>\n");
799
}
800
801
cupsdCloseCreatedConfFile(fp, filename);
802
}
803
804