2 * "$Id: classes.c 9793 2011-05-20 03:49:49Z mike $"
4 * Printer class routines for the CUPS scheduler.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * cupsdAddClass() - Add a class to the system.
18 * cupsdAddPrinterToClass() - Add a printer to a class...
19 * cupsdDeletePrinterFromClass() - Delete a printer from a class.
20 * cupsdDeletePrinterFromClasses() - Delete a printer from all classes.
21 * cupsdFindAvailablePrinter() - Find an available printer in a class.
22 * cupsdFindClass() - Find the named class.
23 * cupsdLoadAllClasses() - Load classes from the classes.conf file.
24 * cupsdSaveAllClasses() - Save classes to the classes.conf file.
25 * cupsdUpdateImplicitClasses() - Update the accepting state of implicit
30 * Include necessary headers...
37 * 'cupsdAddClass()' - Add a class to the system.
40 cupsd_printer_t * /* O - New class */
41 cupsdAddClass(const char *name) /* I - Name of class */
43 cupsd_printer_t *c; /* New class */
44 char uri[1024]; /* Class URI */
48 * Add the printer and set the type to "class"...
51 if ((c = cupsdAddPrinter(name)) != NULL)
54 * Change from a printer to a class...
57 c->type = CUPS_PRINTER_CLASS;
59 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
60 ServerName, RemotePort, "/classes/%s", name);
61 cupsdSetString(&c->uri, uri);
63 cupsdSetString(&c->error_policy, "retry-current-job");
71 * 'cupsdAddPrinterToClass()' - Add a printer to a class...
75 cupsdAddPrinterToClass(
76 cupsd_printer_t *c, /* I - Class to add to */
77 cupsd_printer_t *p) /* I - Printer to add */
79 int i; /* Looping var */
80 cupsd_printer_t **temp; /* Pointer to printer array */
84 * See if this printer is already a member of the class...
87 for (i = 0; i < c->num_printers; i ++)
88 if (c->printers[i] == p)
92 * Allocate memory as needed...
95 if (c->num_printers == 0)
96 temp = malloc(sizeof(cupsd_printer_t *));
98 temp = realloc(c->printers, sizeof(cupsd_printer_t *) * (c->num_printers + 1));
102 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add printer %s to class %s!",
108 * Add the printer to the end of the array and update the number of printers.
112 temp += c->num_printers;
120 * 'cupsdDeletePrinterFromClass()' - Delete a printer from a class.
123 int /* O - 1 if class changed, 0 otherwise */
124 cupsdDeletePrinterFromClass(
125 cupsd_printer_t *c, /* I - Class to delete from */
126 cupsd_printer_t *p) /* I - Printer to delete */
128 int i; /* Looping var */
132 * See if the printer is in the class...
135 for (i = 0; i < c->num_printers; i ++)
136 if (p == c->printers[i])
140 * If it is, remove it from the list...
143 if (i < c->num_printers)
146 * Yes, remove the printer...
150 if (i < c->num_printers)
151 memmove(c->printers + i, c->printers + i + 1,
152 (c->num_printers - i) * sizeof(cupsd_printer_t *));
158 * Update the IPP attributes (have to do this for member-names)...
161 cupsdSetPrinterAttrs(c);
168 * 'cupsdDeletePrinterFromClasses()' - Delete a printer from all classes.
171 int /* O - 1 if class changed, 0 otherwise */
172 cupsdDeletePrinterFromClasses(
173 cupsd_printer_t *p) /* I - Printer to delete */
175 int changed = 0; /* Any class changed? */
176 cupsd_printer_t *c; /* Pointer to current class */
180 * Loop through the printer/class list and remove the printer
181 * from each class listed...
184 for (c = (cupsd_printer_t *)cupsArrayFirst(Printers);
186 c = (cupsd_printer_t *)cupsArrayNext(Printers))
187 if (c->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
188 changed |= cupsdDeletePrinterFromClass(c, p);
191 * Then clean out any empty implicit classes...
194 for (c = (cupsd_printer_t *)cupsArrayFirst(ImplicitPrinters);
196 c = (cupsd_printer_t *)cupsArrayNext(ImplicitPrinters))
197 if (c->num_printers == 0)
199 cupsdLogMessage(CUPSD_LOG_DEBUG, "Deleting implicit class \"%s\"...",
201 cupsdDeletePrinter(c, 0);
210 * 'cupsdFindAvailablePrinter()' - Find an available printer in a class.
213 cupsd_printer_t * /* O - Available printer or NULL */
214 cupsdFindAvailablePrinter(
215 const char *name) /* I - Class to check */
217 int i; /* Looping var */
218 cupsd_printer_t *c; /* Printer class */
225 if ((c = cupsdFindClass(name)) == NULL)
227 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to find class \"%s\"!", name);
231 if (c->num_printers == 0)
235 * Make sure that the last printer is also a valid index into the printer
236 * array. If not, reset the last printer to 0...
239 if (c->last_printer >= c->num_printers)
243 * Loop through the printers in the class and return the first idle
244 * printer... We keep track of the last printer that we used so that
245 * a "round robin" type of scheduling is realized (otherwise the first
246 * server might be saturated with print jobs...)
248 * Thanks to Joel Fredrikson for helping us get this right!
251 for (i = c->last_printer + 1; ; i ++)
253 if (i >= c->num_printers)
256 if (c->printers[i]->accepting &&
257 (c->printers[i]->state == IPP_PRINTER_IDLE ||
258 ((c->printers[i]->type & CUPS_PRINTER_REMOTE) && !c->printers[i]->job)))
261 return (c->printers[i]);
264 if (i == c->last_printer)
273 * 'cupsdFindClass()' - Find the named class.
276 cupsd_printer_t * /* O - Matching class or NULL */
277 cupsdFindClass(const char *name) /* I - Name of class */
279 cupsd_printer_t *c; /* Current class/printer */
282 if ((c = cupsdFindDest(name)) != NULL &&
283 (c->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)))
291 * 'cupsdLoadAllClasses()' - Load classes from the classes.conf file.
295 cupsdLoadAllClasses(void)
297 int i; /* Looping var */
298 cups_file_t *fp; /* classes.conf file */
299 int linenum; /* Current line number */
300 char line[4096], /* Line from file */
301 *value, /* Pointer to value */
302 *valueptr; /* Pointer into value */
303 cupsd_printer_t *p, /* Current printer class */
304 *temp; /* Temporary pointer to printer */
308 * Open the classes.conf file...
311 snprintf(line, sizeof(line), "%s/classes.conf", ServerRoot);
312 if ((fp = cupsdOpenConfFile(line)) == NULL)
316 * Read class configurations until we hit EOF...
322 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
325 * Decode the directive...
328 if (!_cups_strcasecmp(line, "<Class") ||
329 !_cups_strcasecmp(line, "<DefaultClass"))
332 * <Class name> or <DefaultClass name>
335 if (p == NULL && value)
337 cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value);
340 * Since prior classes may have implicitly defined this class,
341 * see if it already exists...
344 if ((p = cupsdFindDest(value)) != NULL)
346 p->type = CUPS_PRINTER_CLASS;
347 cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName,
349 cupsdSetString(&p->error_policy, "retry-job");
352 p = cupsdAddClass(value);
355 p->state = IPP_PRINTER_IDLE;
357 if (!_cups_strcasecmp(line, "<DefaultClass"))
361 cupsdLogMessage(CUPSD_LOG_ERROR,
362 "Syntax error on line %d of classes.conf.", linenum);
364 else if (!_cups_strcasecmp(line, "</Class>"))
368 cupsdSetPrinterAttrs(p);
372 cupsdLogMessage(CUPSD_LOG_ERROR,
373 "Syntax error on line %d of classes.conf.", linenum);
377 cupsdLogMessage(CUPSD_LOG_ERROR,
378 "Syntax error on line %d of classes.conf.", linenum);
380 else if (!_cups_strcasecmp(line, "UUID"))
382 if (value && !strncmp(value, "urn:uuid:", 9))
383 cupsdSetString(&(p->uuid), value);
385 cupsdLogMessage(CUPSD_LOG_ERROR,
386 "Bad UUID on line %d of classes.conf.", linenum);
388 else if (!_cups_strcasecmp(line, "AuthInfoRequired"))
390 if (!cupsdSetAuthInfoRequired(p, value, NULL))
391 cupsdLogMessage(CUPSD_LOG_ERROR,
392 "Bad AuthInfoRequired on line %d of classes.conf.",
395 else if (!_cups_strcasecmp(line, "Info"))
398 cupsdSetString(&p->info, value);
400 else if (!_cups_strcasecmp(line, "Location"))
403 cupsdSetString(&p->location, value);
405 else if (!_cups_strcasecmp(line, "Option") && value)
411 for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
414 cupsdLogMessage(CUPSD_LOG_ERROR,
415 "Syntax error on line %d of classes.conf.", linenum);
418 for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
420 p->num_options = cupsAddOption(value, valueptr, p->num_options,
424 else if (!_cups_strcasecmp(line, "Printer"))
428 cupsdLogMessage(CUPSD_LOG_ERROR,
429 "Syntax error on line %d of classes.conf.", linenum);
432 else if ((temp = cupsdFindPrinter(value)) == NULL)
434 cupsdLogMessage(CUPSD_LOG_WARN,
435 "Unknown printer %s on line %d of classes.conf.",
439 * Add the missing remote printer...
442 if ((temp = cupsdAddPrinter(value)) != NULL)
444 cupsdSetString(&temp->make_model, "Remote Printer on unknown");
446 temp->state = IPP_PRINTER_STOPPED;
447 temp->type |= CUPS_PRINTER_REMOTE;
448 temp->browse_time = 2147483647;
450 cupsdSetString(&temp->location, "Location Unknown");
451 cupsdSetString(&temp->info, "No Information Available");
452 temp->hostname[0] = '\0';
454 cupsdSetPrinterAttrs(temp);
459 cupsdAddPrinterToClass(p, temp);
461 else if (!_cups_strcasecmp(line, "State"))
464 * Set the initial queue state...
467 if (!_cups_strcasecmp(value, "idle"))
468 p->state = IPP_PRINTER_IDLE;
469 else if (!_cups_strcasecmp(value, "stopped"))
471 p->state = IPP_PRINTER_STOPPED;
473 for (i = 0 ; i < p->num_reasons; i ++)
474 if (!strcmp("paused", p->reasons[i]))
477 if (i >= p->num_reasons &&
478 p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
480 p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
485 cupsdLogMessage(CUPSD_LOG_ERROR,
486 "Syntax error on line %d of classes.conf.",
489 else if (!_cups_strcasecmp(line, "StateMessage"))
492 * Set the initial queue state message...
496 strlcpy(p->state_message, value, sizeof(p->state_message));
498 else if (!_cups_strcasecmp(line, "StateTime"))
501 * Set the state time...
505 p->state_time = atoi(value);
507 else if (!_cups_strcasecmp(line, "Accepting"))
510 * Set the initial accepting state...
514 (!_cups_strcasecmp(value, "yes") ||
515 !_cups_strcasecmp(value, "on") ||
516 !_cups_strcasecmp(value, "true")))
519 (!_cups_strcasecmp(value, "no") ||
520 !_cups_strcasecmp(value, "off") ||
521 !_cups_strcasecmp(value, "false")))
524 cupsdLogMessage(CUPSD_LOG_ERROR,
525 "Syntax error on line %d of classes.conf.",
528 else if (!_cups_strcasecmp(line, "Shared"))
531 * Set the initial shared state...
535 (!_cups_strcasecmp(value, "yes") ||
536 !_cups_strcasecmp(value, "on") ||
537 !_cups_strcasecmp(value, "true")))
540 (!_cups_strcasecmp(value, "no") ||
541 !_cups_strcasecmp(value, "off") ||
542 !_cups_strcasecmp(value, "false")))
545 cupsdLogMessage(CUPSD_LOG_ERROR,
546 "Syntax error on line %d of classes.conf.",
549 else if (!_cups_strcasecmp(line, "JobSheets"))
552 * Set the initial job sheets...
557 for (valueptr = value;
558 *valueptr && !isspace(*valueptr & 255);
564 cupsdSetString(&p->job_sheets[0], value);
566 while (isspace(*valueptr & 255))
571 for (value = valueptr;
572 *valueptr && !isspace(*valueptr & 255);
578 cupsdSetString(&p->job_sheets[1], value);
582 cupsdLogMessage(CUPSD_LOG_ERROR,
583 "Syntax error on line %d of classes.conf.", linenum);
585 else if (!_cups_strcasecmp(line, "AllowUser"))
590 cupsdAddString(&(p->users), value);
593 cupsdLogMessage(CUPSD_LOG_ERROR,
594 "Syntax error on line %d of classes.conf.", linenum);
596 else if (!_cups_strcasecmp(line, "DenyUser"))
601 cupsdAddString(&(p->users), value);
604 cupsdLogMessage(CUPSD_LOG_ERROR,
605 "Syntax error on line %d of classes.conf.", linenum);
607 else if (!_cups_strcasecmp(line, "QuotaPeriod"))
610 p->quota_period = atoi(value);
612 cupsdLogMessage(CUPSD_LOG_ERROR,
613 "Syntax error on line %d of classes.conf.", linenum);
615 else if (!_cups_strcasecmp(line, "PageLimit"))
618 p->page_limit = atoi(value);
620 cupsdLogMessage(CUPSD_LOG_ERROR,
621 "Syntax error on line %d of classes.conf.", linenum);
623 else if (!_cups_strcasecmp(line, "KLimit"))
626 p->k_limit = atoi(value);
628 cupsdLogMessage(CUPSD_LOG_ERROR,
629 "Syntax error on line %d of classes.conf.", linenum);
631 else if (!_cups_strcasecmp(line, "OpPolicy"))
635 cupsd_policy_t *pol; /* Policy */
638 if ((pol = cupsdFindPolicy(value)) != NULL)
640 cupsdSetString(&p->op_policy, value);
641 p->op_policy_ptr = pol;
644 cupsdLogMessage(CUPSD_LOG_ERROR,
645 "Bad policy \"%s\" on line %d of classes.conf",
649 cupsdLogMessage(CUPSD_LOG_ERROR,
650 "Syntax error on line %d of classes.conf.", linenum);
652 else if (!_cups_strcasecmp(line, "ErrorPolicy"))
656 if (strcmp(value, "retry-current-job") && strcmp(value, "retry-job"))
657 cupsdLogMessage(CUPSD_LOG_WARN,
658 "ErrorPolicy %s ignored on line %d of classes.conf",
662 cupsdLogMessage(CUPSD_LOG_ERROR,
663 "Syntax error on line %d of classes.conf.", linenum);
668 * Something else we don't understand...
671 cupsdLogMessage(CUPSD_LOG_ERROR,
672 "Unknown configuration directive %s on line %d of classes.conf.",
682 * 'cupsdSaveAllClasses()' - Save classes to the classes.conf file.
686 cupsdSaveAllClasses(void)
688 cups_file_t *fp; /* classes.conf file */
689 char filename[1024], /* classes.conf filename */
690 temp[1024], /* Temporary string */
691 value[2048], /* Value string */
692 *name; /* Current user name */
693 cupsd_printer_t *pclass; /* Current printer class */
694 int i; /* Looping var */
695 time_t curtime; /* Current time */
696 struct tm *curdate; /* Current date */
697 cups_option_t *option; /* Current option */
701 * Create the classes.conf file...
704 snprintf(filename, sizeof(filename), "%s/classes.conf", ServerRoot);
706 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
709 cupsdLogMessage(CUPSD_LOG_INFO, "Saving classes.conf...");
712 * Write a small header to the file...
715 curtime = time(NULL);
716 curdate = localtime(&curtime);
717 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
719 cupsFilePuts(fp, "# Class configuration file for " CUPS_SVERSION "\n");
720 cupsFilePrintf(fp, "# Written by cupsd\n");
721 cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
724 * Write each local class known to the system...
727 for (pclass = (cupsd_printer_t *)cupsArrayFirst(Printers);
729 pclass = (cupsd_printer_t *)cupsArrayNext(Printers))
732 * Skip remote destinations and regular printers...
735 if ((pclass->type & CUPS_PRINTER_REMOTE) ||
736 (pclass->type & CUPS_PRINTER_IMPLICIT) ||
737 !(pclass->type & CUPS_PRINTER_CLASS))
741 * Write printers as needed...
744 if (pclass == DefaultPrinter)
745 cupsFilePrintf(fp, "<DefaultClass %s>\n", pclass->name);
747 cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
749 cupsFilePrintf(fp, "UUID %s\n", pclass->uuid);
751 if (pclass->num_auth_info_required > 0)
753 switch (pclass->num_auth_info_required)
756 strlcpy(value, pclass->auth_info_required[0], sizeof(value));
760 snprintf(value, sizeof(value), "%s,%s",
761 pclass->auth_info_required[0],
762 pclass->auth_info_required[1]);
767 snprintf(value, sizeof(value), "%s,%s,%s",
768 pclass->auth_info_required[0],
769 pclass->auth_info_required[1],
770 pclass->auth_info_required[2]);
774 cupsFilePutConf(fp, "AuthInfoRequired", value);
778 cupsFilePutConf(fp, "Info", pclass->info);
780 if (pclass->location)
781 cupsFilePutConf(fp, "Location", pclass->location);
783 if (pclass->state == IPP_PRINTER_STOPPED)
784 cupsFilePuts(fp, "State Stopped\n");
786 cupsFilePuts(fp, "State Idle\n");
788 cupsFilePrintf(fp, "StateTime %d\n", (int)pclass->state_time);
790 if (pclass->accepting)
791 cupsFilePuts(fp, "Accepting Yes\n");
793 cupsFilePuts(fp, "Accepting No\n");
796 cupsFilePuts(fp, "Shared Yes\n");
798 cupsFilePuts(fp, "Shared No\n");
800 snprintf(value, sizeof(value), "%s %s", pclass->job_sheets[0],
801 pclass->job_sheets[1]);
802 cupsFilePutConf(fp, "JobSheets", value);
804 for (i = 0; i < pclass->num_printers; i ++)
805 cupsFilePrintf(fp, "Printer %s\n", pclass->printers[i]->name);
807 cupsFilePrintf(fp, "QuotaPeriod %d\n", pclass->quota_period);
808 cupsFilePrintf(fp, "PageLimit %d\n", pclass->page_limit);
809 cupsFilePrintf(fp, "KLimit %d\n", pclass->k_limit);
811 for (name = (char *)cupsArrayFirst(pclass->users);
813 name = (char *)cupsArrayNext(pclass->users))
814 cupsFilePutConf(fp, pclass->deny_users ? "DenyUser" : "AllowUser", name);
816 if (pclass->op_policy)
817 cupsFilePutConf(fp, "OpPolicy", pclass->op_policy);
818 if (pclass->error_policy)
819 cupsFilePutConf(fp, "ErrorPolicy", pclass->error_policy);
821 for (i = pclass->num_options, option = pclass->options;
825 snprintf(value, sizeof(value), "%s %s", option->name, option->value);
826 cupsFilePutConf(fp, "Option", value);
829 cupsFilePuts(fp, "</Class>\n");
832 cupsdCloseCreatedConfFile(fp, filename);
837 * 'cupsdUpdateImplicitClasses()' - Update the accepting state of implicit
842 cupsdUpdateImplicitClasses(void)
844 int i; /* Looping var */
845 cupsd_printer_t *pclass; /* Current class */
846 int accepting; /* printer-is-accepting-jobs value */
849 for (pclass = (cupsd_printer_t *)cupsArrayFirst(ImplicitPrinters);
851 pclass = (cupsd_printer_t *)cupsArrayNext(ImplicitPrinters))
854 * Loop through the printers to come up with a composite state...
857 for (i = 0, accepting = 0; i < pclass->num_printers; i ++)
858 if ((accepting = pclass->printers[i]->accepting) != 0)
861 pclass->accepting = accepting;
867 * End of "$Id: classes.c 9793 2011-05-20 03:49:49Z mike $".