]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/classes.c
2 * Printer class routines for the CUPS scheduler.
4 * Copyright © 2020-2025 by OpenPrinting.
5 * Copyright © 2007-2017 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
13 * Include necessary headers...
20 * 'cupsdAddClass()' - Add a class to the system.
23 cupsd_printer_t
* /* O - New class */
24 cupsdAddClass(const char *name
) /* I - Name of class */
26 cupsd_printer_t
*c
; /* New class */
27 char uri
[1024]; /* Class URI */
31 * Add the printer and set the type to "class"...
34 if ((c
= cupsdAddPrinter(name
)) != NULL
)
37 * Change from a printer to a class...
40 c
->type
= CUPS_PTYPE_CLASS
;
42 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
43 ServerName
, RemotePort
, "/classes/%s", name
);
44 cupsdSetString(&c
->uri
, uri
);
46 cupsdSetString(&c
->error_policy
, "retry-current-job");
54 * 'cupsdAddPrinterToClass()' - Add a printer to a class...
58 cupsdAddPrinterToClass(
59 cupsd_printer_t
*c
, /* I - Class to add to */
60 cupsd_printer_t
*p
) /* I - Printer to add */
62 int i
; /* Looping var */
63 cupsd_printer_t
**temp
; /* Pointer to printer array */
67 * See if this printer is already a member of the class...
70 for (i
= 0; i
< c
->num_printers
; i
++)
71 if (c
->printers
[i
] == p
)
75 * Allocate memory as needed...
78 if (c
->num_printers
== 0)
79 temp
= malloc(sizeof(cupsd_printer_t
*));
81 temp
= realloc(c
->printers
, sizeof(cupsd_printer_t
*) * (size_t)(c
->num_printers
+ 1));
85 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add printer %s to class %s!",
91 * Add the printer to the end of the array and update the number of printers.
95 temp
+= c
->num_printers
;
103 * 'cupsdDeletePrinterFromClass()' - Delete a printer from a class.
106 int /* O - 1 if class changed, 0 otherwise */
107 cupsdDeletePrinterFromClass(
108 cupsd_printer_t
*c
, /* I - Class to delete from */
109 cupsd_printer_t
*p
) /* I - Printer to delete */
111 int i
; /* Looping var */
115 * See if the printer is in the class...
118 for (i
= 0; i
< c
->num_printers
; i
++)
119 if (p
== c
->printers
[i
])
123 * If it is, remove it from the list...
126 if (i
< c
->num_printers
)
129 * Yes, remove the printer...
133 if (i
< c
->num_printers
)
134 memmove(c
->printers
+ i
, c
->printers
+ i
+ 1,
135 (size_t)(c
->num_printers
- i
) * sizeof(cupsd_printer_t
*));
141 * Update the IPP attributes (have to do this for member-names)...
144 cupsdSetPrinterAttrs(c
);
151 * 'cupsdDeletePrinterFromClasses()' - Delete a printer from all classes.
154 int /* O - 1 if class changed, 0 otherwise */
155 cupsdDeletePrinterFromClasses(
156 cupsd_printer_t
*p
) /* I - Printer to delete */
158 int changed
= 0; /* Any class changed? */
159 cupsd_printer_t
*c
; /* Pointer to current class */
163 * Loop through the printer/class list and remove the printer
164 * from each class listed...
167 for (c
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
169 c
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
170 if (c
->type
& CUPS_PTYPE_CLASS
)
171 changed
|= cupsdDeletePrinterFromClass(c
, p
);
178 * 'cupsdFindAvailablePrinter()' - Find an available printer in a class.
181 cupsd_printer_t
* /* O - Available printer or NULL */
182 cupsdFindAvailablePrinter(
183 const char *name
) /* I - Class to check */
185 int i
; /* Looping var */
186 cupsd_printer_t
*c
; /* Printer class */
193 if ((c
= cupsdFindClass(name
)) == NULL
)
195 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to find class \"%s\"!", name
);
199 if (c
->num_printers
== 0)
203 * Make sure that the last printer is also a valid index into the printer
204 * array. If not, reset the last printer to 0...
207 if (c
->last_printer
>= c
->num_printers
)
211 * Loop through the printers in the class and return the first idle
212 * printer... We keep track of the last printer that we used so that
213 * a "round robin" type of scheduling is realized (otherwise the first
214 * server might be saturated with print jobs...)
216 * Thanks to Joel Fredrikson for helping us get this right!
219 for (i
= c
->last_printer
+ 1; ; i
++)
221 if (i
>= c
->num_printers
)
224 if (c
->printers
[i
]->accepting
&&
225 (c
->printers
[i
]->state
== IPP_PSTATE_IDLE
||
226 ((c
->printers
[i
]->type
& CUPS_PTYPE_REMOTE
) && !c
->printers
[i
]->job
)))
229 return (c
->printers
[i
]);
232 if (i
== c
->last_printer
)
241 * 'cupsdFindClass()' - Find the named class.
244 cupsd_printer_t
* /* O - Matching class or NULL */
245 cupsdFindClass(const char *name
) /* I - Name of class */
247 cupsd_printer_t
*c
; /* Current class/printer */
250 if ((c
= cupsdFindDest(name
)) != NULL
&& (c
->type
& CUPS_PTYPE_CLASS
))
258 * 'cupsdLoadAllClasses()' - Load classes from the classes.conf file.
262 cupsdLoadAllClasses(void)
264 int i
; /* Looping var */
265 cups_file_t
*fp
; /* classes.conf file */
266 int linenum
; /* Current line number */
267 char line
[4096], /* Line from file */
268 *value
, /* Pointer to value */
269 *valueptr
; /* Pointer into value */
270 cupsd_printer_t
*p
, /* Current printer class */
271 *temp
; /* Temporary pointer to printer */
275 * Open the classes.conf file...
278 snprintf(line
, sizeof(line
), "%s/classes.conf", ServerRoot
);
279 if ((fp
= cupsdOpenConfFile(line
)) == NULL
)
283 * Read class configurations until we hit EOF...
289 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
292 * Decode the directive...
295 if (!_cups_strcasecmp(line
, "<Class") ||
296 !_cups_strcasecmp(line
, "<DefaultClass"))
299 * <Class name> or <DefaultClass name>
302 if (p
== NULL
&& value
)
304 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading class %s...", value
);
307 * Since prior classes may have implicitly defined this class,
308 * see if it already exists...
311 if ((p
= cupsdFindDest(value
)) != NULL
)
313 p
->type
= CUPS_PTYPE_CLASS
;
314 cupsdSetStringf(&p
->uri
, "ipp://%s:%d/classes/%s", ServerName
,
316 cupsdSetString(&p
->error_policy
, "retry-job");
319 p
= cupsdAddClass(value
);
322 p
->state
= IPP_PSTATE_IDLE
;
324 if (!_cups_strcasecmp(line
, "<DefaultClass"))
328 cupsdLogMessage(CUPSD_LOG_ERROR
,
329 "Syntax error on line %d of classes.conf.", linenum
);
331 else if (!_cups_strcasecmp(line
, "</Class>") || !_cups_strcasecmp(line
, "</DefaultClass>"))
335 cupsdSetPrinterAttrs(p
);
339 cupsdLogMessage(CUPSD_LOG_ERROR
,
340 "Syntax error on line %d of classes.conf.", linenum
);
344 cupsdLogMessage(CUPSD_LOG_ERROR
,
345 "Syntax error on line %d of classes.conf.", linenum
);
347 else if (!_cups_strcasecmp(line
, "PrinterId"))
349 if (value
&& (i
= atoi(value
)) > 0)
352 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad PrinterId on line %d of classes.conf.", linenum
);
354 else if (!_cups_strcasecmp(line
, "UUID"))
356 if (value
&& !strncmp(value
, "urn:uuid:", 9))
357 cupsdSetString(&(p
->uuid
), value
);
359 cupsdLogMessage(CUPSD_LOG_ERROR
,
360 "Bad UUID on line %d of classes.conf.", linenum
);
362 else if (!_cups_strcasecmp(line
, "AuthInfoRequired"))
364 if (!cupsdSetAuthInfoRequired(p
, value
, NULL
))
365 cupsdLogMessage(CUPSD_LOG_ERROR
,
366 "Bad AuthInfoRequired on line %d of classes.conf.",
369 else if (!_cups_strcasecmp(line
, "Info"))
372 cupsdSetString(&p
->info
, value
);
374 else if (!_cups_strcasecmp(line
, "Location"))
377 cupsdSetString(&p
->location
, value
);
379 else if (!_cups_strcasecmp(line
, "Option") && value
)
385 for (valueptr
= value
; *valueptr
&& !isspace(*valueptr
& 255); valueptr
++);
388 cupsdLogMessage(CUPSD_LOG_ERROR
,
389 "Syntax error on line %d of classes.conf.", linenum
);
392 for (; *valueptr
&& isspace(*valueptr
& 255); *valueptr
++ = '\0');
394 p
->num_options
= cupsAddOption(value
, valueptr
, p
->num_options
,
398 else if (!_cups_strcasecmp(line
, "Printer"))
402 cupsdLogMessage(CUPSD_LOG_ERROR
,
403 "Syntax error on line %d of classes.conf.", linenum
);
406 else if ((temp
= cupsdFindPrinter(value
)) == NULL
)
408 cupsdLogMessage(CUPSD_LOG_WARN
,
409 "Unknown printer %s on line %d of classes.conf.",
413 * Add the missing remote printer...
416 if ((temp
= cupsdAddPrinter(value
)) != NULL
)
418 cupsdSetString(&temp
->make_model
, "Remote Printer on unknown");
420 temp
->state
= IPP_PSTATE_STOPPED
;
421 temp
->type
|= CUPS_PTYPE_REMOTE
;
423 cupsdSetString(&temp
->location
, "Location Unknown");
424 cupsdSetString(&temp
->info
, "No Information Available");
425 temp
->hostname
[0] = '\0';
427 cupsdSetPrinterAttrs(temp
);
432 cupsdAddPrinterToClass(p
, temp
);
434 else if (!_cups_strcasecmp(line
, "State"))
437 * Set the initial queue state...
440 if (!_cups_strcasecmp(value
, "idle"))
441 p
->state
= IPP_PSTATE_IDLE
;
442 else if (!_cups_strcasecmp(value
, "stopped"))
444 p
->state
= IPP_PSTATE_STOPPED
;
446 for (i
= 0 ; i
< p
->num_reasons
; i
++)
447 if (!strcmp("paused", p
->reasons
[i
]))
450 if (i
>= p
->num_reasons
&&
451 p
->num_reasons
< (int)(sizeof(p
->reasons
) / sizeof(p
->reasons
[0])))
453 p
->reasons
[p
->num_reasons
] = _cupsStrAlloc("paused");
458 cupsdLogMessage(CUPSD_LOG_ERROR
,
459 "Syntax error on line %d of classes.conf.",
462 else if (!_cups_strcasecmp(line
, "StateMessage"))
465 * Set the initial queue state message...
469 cupsCopyString(p
->state_message
, value
, sizeof(p
->state_message
));
471 else if (!_cups_strcasecmp(line
, "StateTime"))
474 * Set the state time...
478 p
->state_time
= atoi(value
);
480 else if (!_cups_strcasecmp(line
, "Accepting"))
483 * Set the initial accepting state...
487 (!_cups_strcasecmp(value
, "yes") ||
488 !_cups_strcasecmp(value
, "on") ||
489 !_cups_strcasecmp(value
, "true")))
492 (!_cups_strcasecmp(value
, "no") ||
493 !_cups_strcasecmp(value
, "off") ||
494 !_cups_strcasecmp(value
, "false")))
497 cupsdLogMessage(CUPSD_LOG_ERROR
,
498 "Syntax error on line %d of classes.conf.",
501 else if (!_cups_strcasecmp(line
, "Shared"))
504 * Set the initial shared state...
508 (!_cups_strcasecmp(value
, "yes") ||
509 !_cups_strcasecmp(value
, "on") ||
510 !_cups_strcasecmp(value
, "true")))
513 (!_cups_strcasecmp(value
, "no") ||
514 !_cups_strcasecmp(value
, "off") ||
515 !_cups_strcasecmp(value
, "false")))
518 cupsdLogMessage(CUPSD_LOG_ERROR
,
519 "Syntax error on line %d of classes.conf.",
522 else if (!_cups_strcasecmp(line
, "JobSheets"))
525 * Set the initial job sheets...
530 for (valueptr
= value
;
531 *valueptr
&& !isspace(*valueptr
& 255);
537 cupsdSetString(&p
->job_sheets
[0], value
);
539 while (isspace(*valueptr
& 255))
544 for (value
= valueptr
;
545 *valueptr
&& !isspace(*valueptr
& 255);
551 cupsdSetString(&p
->job_sheets
[1], value
);
555 cupsdLogMessage(CUPSD_LOG_ERROR
,
556 "Syntax error on line %d of classes.conf.", linenum
);
558 else if (!_cups_strcasecmp(line
, "AllowUser"))
563 cupsdAddString(&(p
->users
), value
);
566 cupsdLogMessage(CUPSD_LOG_ERROR
,
567 "Syntax error on line %d of classes.conf.", linenum
);
569 else if (!_cups_strcasecmp(line
, "DenyUser"))
574 cupsdAddString(&(p
->users
), value
);
577 cupsdLogMessage(CUPSD_LOG_ERROR
,
578 "Syntax error on line %d of classes.conf.", linenum
);
580 else if (!_cups_strcasecmp(line
, "QuotaPeriod"))
583 p
->quota_period
= atoi(value
);
585 cupsdLogMessage(CUPSD_LOG_ERROR
,
586 "Syntax error on line %d of classes.conf.", linenum
);
588 else if (!_cups_strcasecmp(line
, "PageLimit"))
591 p
->page_limit
= atoi(value
);
593 cupsdLogMessage(CUPSD_LOG_ERROR
,
594 "Syntax error on line %d of classes.conf.", linenum
);
596 else if (!_cups_strcasecmp(line
, "KLimit"))
599 p
->k_limit
= atoi(value
);
601 cupsdLogMessage(CUPSD_LOG_ERROR
,
602 "Syntax error on line %d of classes.conf.", linenum
);
604 else if (!_cups_strcasecmp(line
, "OpPolicy"))
608 cupsd_policy_t
*pol
; /* Policy */
611 if ((pol
= cupsdFindPolicy(value
)) != NULL
)
613 cupsdSetString(&p
->op_policy
, value
);
614 p
->op_policy_ptr
= pol
;
617 cupsdLogMessage(CUPSD_LOG_ERROR
,
618 "Bad policy \"%s\" on line %d of classes.conf",
622 cupsdLogMessage(CUPSD_LOG_ERROR
,
623 "Syntax error on line %d of classes.conf.", linenum
);
625 else if (!_cups_strcasecmp(line
, "ErrorPolicy"))
629 if (strcmp(value
, "retry-current-job") && strcmp(value
, "retry-job"))
630 cupsdLogMessage(CUPSD_LOG_WARN
,
631 "ErrorPolicy %s ignored on line %d of classes.conf",
635 cupsdLogMessage(CUPSD_LOG_ERROR
,
636 "Syntax error on line %d of classes.conf.", linenum
);
641 * Something else we don't understand...
644 cupsdLogMessage(CUPSD_LOG_ERROR
,
645 "Unknown configuration directive %s on line %d of classes.conf.",
655 * 'cupsdSaveAllClasses()' - Save classes to the classes.conf file.
659 cupsdSaveAllClasses(void)
661 cups_file_t
*fp
; /* classes.conf file */
662 char filename
[1024], /* classes.conf filename */
663 value
[2048], /* Value string */
664 *name
; /* Current user name */
665 cupsd_printer_t
*pclass
; /* Current printer class */
666 int i
, j
, /* Looping vars */
667 pcount
; /* Number of printers */
668 cups_option_t
*option
; /* Current option */
672 * Create the classes.conf file...
675 snprintf(filename
, sizeof(filename
), "%s/classes.conf", ServerRoot
);
677 if ((fp
= cupsdCreateConfFile(filename
, ConfigFilePerm
)) == NULL
)
680 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving classes.conf...");
683 * Write a small header to the file...
686 cupsFilePuts(fp
, "# Class configuration file for " CUPS_SVERSION
"\n");
687 cupsFilePrintf(fp
, "# Written by cupsd\n");
688 cupsFilePuts(fp
, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
691 * Write each local class known to the system...
694 cupsRWLockRead(&PrintersLock
);
696 for (i
= 0, pcount
= cupsArrayGetCount(Printers
); i
< pcount
; i
++)
699 * Skip remote destinations and regular printers...
702 pclass
= (cupsd_printer_t
*)cupsArrayGetElement(Printers
, i
);
704 if ((pclass
->type
& CUPS_PTYPE_REMOTE
) ||
705 !(pclass
->type
& CUPS_PTYPE_CLASS
))
709 * Write printers as needed...
712 if (pclass
== DefaultPrinter
)
713 cupsFilePrintf(fp
, "<DefaultClass %s>\n", pclass
->name
);
715 cupsFilePrintf(fp
, "<Class %s>\n", pclass
->name
);
717 if (pclass
->printer_id
)
718 cupsFilePrintf(fp
, "PrinterId %d\n", pclass
->printer_id
);
720 cupsFilePrintf(fp
, "UUID %s\n", pclass
->uuid
);
722 if (pclass
->num_auth_info_required
> 0)
724 switch (pclass
->num_auth_info_required
)
727 cupsCopyString(value
, pclass
->auth_info_required
[0], sizeof(value
));
731 snprintf(value
, sizeof(value
), "%s,%s",
732 pclass
->auth_info_required
[0],
733 pclass
->auth_info_required
[1]);
738 snprintf(value
, sizeof(value
), "%s,%s,%s",
739 pclass
->auth_info_required
[0],
740 pclass
->auth_info_required
[1],
741 pclass
->auth_info_required
[2]);
745 cupsFilePutConf(fp
, "AuthInfoRequired", value
);
749 cupsFilePutConf(fp
, "Info", pclass
->info
);
751 if (pclass
->location
)
752 cupsFilePutConf(fp
, "Location", pclass
->location
);
754 if (pclass
->state
== IPP_PSTATE_STOPPED
)
755 cupsFilePuts(fp
, "State Stopped\n");
757 cupsFilePuts(fp
, "State Idle\n");
759 cupsFilePrintf(fp
, "StateTime %d\n", (int)pclass
->state_time
);
761 if (pclass
->accepting
)
762 cupsFilePuts(fp
, "Accepting Yes\n");
764 cupsFilePuts(fp
, "Accepting No\n");
767 cupsFilePuts(fp
, "Shared Yes\n");
769 cupsFilePuts(fp
, "Shared No\n");
771 snprintf(value
, sizeof(value
), "%s %s", pclass
->job_sheets
[0],
772 pclass
->job_sheets
[1]);
773 cupsFilePutConf(fp
, "JobSheets", value
);
775 for (j
= 0; j
< pclass
->num_printers
; j
++)
776 cupsFilePrintf(fp
, "Printer %s\n", pclass
->printers
[j
]->name
);
778 cupsFilePrintf(fp
, "QuotaPeriod %d\n", pclass
->quota_period
);
779 cupsFilePrintf(fp
, "PageLimit %d\n", pclass
->page_limit
);
780 cupsFilePrintf(fp
, "KLimit %d\n", pclass
->k_limit
);
782 for (name
= (char *)cupsArrayFirst(pclass
->users
);
784 name
= (char *)cupsArrayNext(pclass
->users
))
785 cupsFilePutConf(fp
, pclass
->deny_users
? "DenyUser" : "AllowUser", name
);
787 if (pclass
->op_policy
)
788 cupsFilePutConf(fp
, "OpPolicy", pclass
->op_policy
);
789 if (pclass
->error_policy
)
790 cupsFilePutConf(fp
, "ErrorPolicy", pclass
->error_policy
);
792 for (j
= pclass
->num_options
, option
= pclass
->options
;
796 snprintf(value
, sizeof(value
), "%s %s", option
->name
, option
->value
);
797 cupsFilePutConf(fp
, "Option", value
);
800 if (pclass
== DefaultPrinter
)
801 cupsFilePuts(fp
, "</DefaultClass>\n");
803 cupsFilePuts(fp
, "</Class>\n");
806 cupsRWUnlock(&PrintersLock
);
808 cupsdCloseCreatedConfFile(fp
, filename
);