%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd %% module #include "printqd.h" $!trace-include #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_STRUCT_SOCKADDR_UN #if DK3_HAVE_SIGSET #if DK3_HAVE_GETPWNAM && DK3_HAVE_GETGRNAM /** Message texts for diagnostics. */ static dkChar const * const pqdconf_messages[] = { $!string-table file=printqdc.str # # 0 String table file name # printqdc.str # # 1, 2 ERROR: Not a limit value! # Not a limit value: " "! # # 3, 4 ERROR: Alias already exists! # Alias already defined: " "! # # 5, 6 ERROR: Printer already exists! # Printer already defined: " "! # # 7, 8 ERROR: Class already defined! # Class already defined: " "! # # 9 ERROR: Missing class name! # Missing class name! # # 10 ERROR: No argument allowed after section title! # No argument allowed after section title! # # 11, 12 ERROR: Illegal configuration section title! # Illegal configuration section title: " "! # # 13 ERROR: Closing bracket is missing! # Closing bracket is missing! # # 14 ERROR: Printer already assigned to a class! # Printer already assigned to a class! # # 15, 16 ERROR: No such class # No such class: " "! # # 17, 18 ERROR: Illegal configuration key! # Illegal configuration key: " "! # # 19, 20 ERROR: Limit for group already defined! # Limit for group " " already defined! # # 21 # Limit must be specified as name:limit! # # 22, 23 ERROR: Limit for user already defined! # Limit for user " " already defined! # # 24 25 ERROR: Illegal deny action! # Illegal deny action: " "! Must be "hold" or "remove"! # # 26 ERROR: Database file name already defined! # Database file name already defined! # # 27 ERROR: Socket file name already defined! # Socket file name already defined! # # 28 ERROR: Info port already defined! # Info port already defined! # # 29, 30 ERROR: Range overflow in port number! # Range overflow in port number: " "! Must be 65535 or less! # # 30, 31 ERROR: Not a number! # Not a number: " "! # # 32 ERROR: User account to run daemon is already defined! # User account to run daemon is already defined! # # 33 ERROR: Group to run daemon is already defined! # Group to run daemon is already defined! # # 34, 35 ERROR: No such user! # No such user: " "! # # 36, 37 ERROR: No such group! # No such group: " "! # # 38 ERROR: Missing value! # Missing value! # # 39, 40 ERROR: Local state directory name too long! # Local state directory name too long: " "! # # 41 ERROR: Failed to find local state directory name! # Failed to find local state directory name! # # 42, 43 ERROR: UNIX domain socket name too long! # UNIX domain socket name too long: " "! $!end }; /** Keywords used by the module. */ static char const * const pqdconf_c8_kw[] = { $!string-table # # 0 File open type read # r # # 1 Keyword "unlimited" # unlimited # # 2 Keyword "deny" # deny # # 3 Default socket file name # /run/printqd/printqd.sock # # 4 Database type mem # mem:: # # 5 Database type ndbm # ndbm:: # # 6 Database type bdb # bdb:: # # 7 Database file name in /var # /printqd/printqd-db $!end }; /** Keywords for deny actions. */ static char const * const pqdconf_deny_actions[] = { $!string-table # 0 remove # 1 hold $!end }; /** Secion type names. */ static char const * const pqdconf_section_types[] = { $!string-table options class printer $!end }; /** Keys in the options section. */ static char const * const pqdconf_section_options[] = { $!string-table # # 0 # database # # 1 # socket # # 2 # info port # # 3 # info allow # # 4 # run as user # # 5 # run as group # # 6 # backlog # # 7 # info backlog $!end }; /** Keys in a class section. */ static char const * const pqdconf_section_class[] = { $!string-table # 0 default limit # 1 group limit # 2 user limit # 3 deny action $!end }; /** Keys in a printer section. */ static char const * const pqdconf_section_printer[] = { $!string-table # 0 class # 1 alias $!end }; dkChar const * const * pqdconf_get_messages(dk3_app_t *app) { dkChar const * const *back = NULL; if(app) { dk3app_messages(app, pqdconf_messages[0], pqdconf_messages); } return back; } /** Destroy limit structure. @param lp Limit structure to destroy. */ static void pqdconf_limit_delete(pqd_limit_t *lp) { $? "+ pqdconf_limit_delete" if(lp) { lp->limit = 0UL; $? ". name = \"%s\"", TR_STR(lp->name) dk3_release(lp->name); dk3_delete(lp); } $? "- pqdconf_limit_delete" } /** Create a new limit structure. @param name User or group name. @param val Limit value. @param app Application structure for diagnostics. @return Pointer to new limit structure on success, NULL on error. */ static pqd_limit_t * pqdconf_limit_new(char const *name, unsigned long val, dk3_app_t *app) { pqd_limit_t *back = NULL; $? "+ pqdconf_limit_new \"%s\" %lu", TR_STR(name), val if(name) { back = dk3_new_app(pqd_limit_t,1,app); if(back) { back->limit = val; back->name = dk3str_c8_dup_app(name, app); if(!(back->name)) { pqdconf_limit_delete(back); back = NULL; } } } $? "- pqdconf_limit_new %s", TR_PTR(back) return back; } /** Compare two limit entries. @param l Left limit object. @param r Right limit object or user name. @param cr Comparison criteria (0=limit/limit by name, 1=limit/name, 2=limit/limit by value largest first). @return Comparison result. */ static int pqdconf_limit_compare(void const *l, void const *r, int cr) { int back = 0; pqd_limit_t const *pl; pqd_limit_t const *pr; if(l) { if(r) { pl = (pqd_limit_t const *)l; switch(cr) { case 2: { pr = (pqd_limit_t const *)r; if(pl->limit > pr->limit) { back = -1; } else { if(pl->limit < pr->limit) { back = 1; } } } break; case 1: { if(pl->name) { back = strcmp(pl->name, (char const *)r); if(back < -1) back = -1; if(back > 1) back = 1; } else back = -1; } break; default: { pr = (pqd_limit_t const *)r; if(pl->name) { if(pr->name) { back = strcmp(pl->name, pr->name); if(back < -1) back = -1; if(back > 1) back = 1; } else back = 1; } else { if(pr->name) back = -1; } } break; } } else back = 1; } else { if(r) back = -1; } return back; } /** Delete class entry. @param cp Class entry to delete. */ static void pqdconf_class_delete(pqd_class_t *cp) { pqd_limit_t *lp; $? "+ pqdconf_class_delete" if(cp) { $? ". name = \"%s\"", TR_STR(cp->name) dk3_release(cp->name); if(cp->s_u) { if(cp->i_u) { dk3sto_it_reset(cp->i_u); while(NULL != (lp = (pqd_limit_t *)dk3sto_it_next(cp->i_u))) { pqdconf_limit_delete(lp); } dk3sto_it_close(cp->i_u); } dk3sto_close(cp->s_u); } cp->s_u = NULL; cp->i_u = NULL; if(cp->s_g) { if(cp->i_g) { dk3sto_it_reset(cp->i_g); while(NULL != (lp = (pqd_limit_t *)dk3sto_it_next(cp->i_g))) { pqdconf_limit_delete(lp); } dk3sto_it_close(cp->i_g); } dk3sto_close(cp->s_g); } cp->s_g = NULL; cp->i_g = NULL; cp->dl = 0UL; cp->da = 0; dk3_delete(cp); } $? "- pqdconf_class_delete" } /** Create new printer class. @param name Class name. @param app Application structure for diagnostics. @return Pointer to new class on success, NULL on error. */ static pqd_class_t * pqdconf_class_new(char const *name, dk3_app_t *app) { pqd_class_t *back = NULL; int ok = 0; $? "+ pqdconf_class_new \"%s\"", TR_STR(name) if(name) { back = dk3_new_app(pqd_class_t,1,app); if(back) { back->s_u = NULL; back->i_u = NULL; back->s_g = NULL; back->i_g = NULL; back->dl = DK3_UL_MAX; /* Default is unlimited printing + accounting. */ back->da = 0; back->name = dk3str_c8_dup_app(name, app); if(back->name) { back->s_u = dk3sto_open_app(app); if(back->s_u) { dk3sto_set_comp(back->s_u, pqdconf_limit_compare, 0); back->i_u = dk3sto_it_open(back->s_u); if(back->i_u) { back->s_g = dk3sto_open_app(app); if(back->s_g) { dk3sto_set_comp(back->s_g, pqdconf_limit_compare, 2); back->i_g = dk3sto_it_open(back->s_g); if(back->i_g) { ok = 1; } } } } } if(!(ok)) { pqdconf_class_delete(back); back = NULL; } } } $? "- pqdconf_class_new %s", TR_PTR(back) return back; } /** Compare two class entries. @param l Left class object. @param r Right class object. @param cr Comparison criteria (0=class/class, 1=class/name). @return Comparison result. */ static int pqdconf_class_compare(void const *l, void const *r, int cr) { int back = 0; pqd_class_t const *pl; pqd_class_t const *pr; if(l) { if(r) { pl = (pqd_class_t const *)l; switch(cr) { case 1: { if(pl->name) { back = dk3str_c8_cmp(pl->name, (char const *)r); if(back < -1) back = -1; if(back > 1) back = 1; } else back = -1; } break; default: { pr = (pqd_class_t const *)r; if(pl->name) { if(pr->name) { back = dk3str_c8_cmp(pl->name, pr->name); if(back < -1) back = -1; if(back > 1) back = 1; } else back = 1; } else { if(pr->name) back = -1; } } break; } } else back = 1; } else { if(r) back = -1; } return back; } /** Delete printer entry. @param pp Printer to delete. */ static void pqdconf_printer_delete(pqd_printer_t *pp) { $? "+ pqdconf_printer_delete" if(pp) { pp->cl = NULL; $? ". name = \"%s\"", TR_STR(pp->name) dk3_release(pp->name); dk3_delete(pp); } $? "- pqdconf_printer_delete" } /** Create new printer entry. @param name Printer name. @param app Application structure for diagnostics. @return Pointer to new printer entry on success, NULL on error. */ static pqd_printer_t * pqdconf_printer_new(char const *name, dk3_app_t *app) { pqd_printer_t *back = NULL; $? "+ pqdconf_printer_new \"%s\"", TR_STR(name) if(name) { back = dk3_new_app(pqd_printer_t,1,app); if(back) { back->cl = NULL; back->name = dk3str_c8_dup_app(name, app); if(!(back->name)) { pqdconf_printer_delete(back); back = NULL; } } } $? "- pqdconf_printer_new %s", TR_PTR(back) return back; } /** Compare two printer entries. @param l Left printer entry. @param r Printer entry or name. @param cr Comparison criteria (0=printer/printer, 1=printer/name). @return Comparison result. */ static int pqdconf_printer_compare(void const *l, void const *r, int cr) { int back = 0; pqd_printer_t const *pl; pqd_printer_t const *pr; if(l) { if(r) { pl = (pqd_printer_t const *)l; switch(cr) { case 1: { if(pl->name) { back = dk3str_c8_cmp(pl->name, (char const *)r); if(back < -1) back = -1; if(back > 1) back = 1; } else back = -1; } break; default: { pr = (pqd_printer_t const *)r; if(pl->name) { if(pr->name) { back = dk3str_c8_cmp(pl->name, pr->name); if(back < -1) back = -1; if(back > 1) back = 1; } else back = 1; } else { if(pr->name) back = -1; } } break; } } else back = 1; } else { if(r) back = -1; } return back; } /** Delete alias entry. @param ap Alias to delete. */ static void pqdconf_alias_delete(pqd_alias_t *ap) { $? "+ pqdconf_alias_delete" if(ap) { ap->pr = NULL; $? ". name = \"%s\"", TR_STR(ap->name) dk3_release(ap->name); dk3_delete(ap); } $? "- pqdconf_alias_delete" } /** Create alias entry. @param name Alias name for printer. @param pr Printer entry. @param app Application structure for diagnostics. @return Pointer no new alias entry on success, NULL on error. */ static pqd_alias_t * pqdconf_alias_new(char const *name, pqd_printer_t *pr, dk3_app_t *app) { pqd_alias_t *back = NULL; $? "+ pqdconf_alias_new \"%s\" %s", TR_STR(name), TR_PTR(pr) if((name) && (pr)) { back = dk3_new_app(pqd_alias_t,1,app); if(back) { back->pr = pr; back->name = dk3str_c8_dup_app(name, app); if(!(back->name)) { pqdconf_alias_delete(back); back = NULL; } } } $? "- pqdconf_alias_new %s", TR_PTR(back) return back; } /** Compare two alias entries. @param l Left alias object. @param r Right alias object or name. @param cr Comparison criteria (0=alias/alias, 1=alias/name). @return Comparison result. */ static int pqdconf_alias_compare(void const *l, void const *r, int cr) { int back = 0; pqd_alias_t const *pl; pqd_alias_t const *pr; if(l) { if(r) { pl = (pqd_alias_t const *)l; switch(cr) { case 1: { if(pl->name) { back = dk3str_c8_cmp(pl->name, (char const *)r); if(back < -1) back = -1; if(back > 1) back = 1; } else back = -1; } break; default: { pr = (pqd_alias_t const *)r; if(pl->name) { if(pr->name) { back = dk3str_c8_cmp(pl->name, pr->name); if(back < -1) back = -1; if(back > 1) back = 1; } else back = 1; } else { if(pr->name) back = -1; } } break; } } else back = 1; } else { if(r) back = -1; } return back; } /** Delete data for allowed peer. @param pp Peer to delete. */ static void pqdconf_peer_delete(dk3_peer_allowed_t *pp) { $? "+ pqdconf_peer_delete" dk3_release(pp); $? "- pqdconf_peer_delete" } /** Create data for allowed peer. @param src Allowed peer (data source). @param app Application structure for diagnostics. @return Pointer to new structure on success, NULL on error. */ static dk3_peer_allowed_t * pqdconf_peer_new(dk3_peer_allowed_t *src, dk3_app_t *app) { dk3_peer_allowed_t *back; $? "+ pqdconf_peer_new" if(src) { back = dk3_new_app(dk3_peer_allowed_t,1,app); if(back) { dk3mem_cpy((void *)back, (void *)src, sizeof(dk3_peer_allowed_t)); } } $? "- pqdconf_peer_new %s", TR_PTR(back) return back; } /** Initialize configuration before reading configuration file. @param cfg Configuration to initialize. */ static void pqdconf_init(pqd_conf_t *cfg) { $? "+ pqdconf_init" dk3mem_res((void *)cfg, sizeof(pqd_conf_t)); cfg->usn = NULL; cfg->dbn = NULL; cfg->s_c = NULL; cfg->i_c = NULL; cfg->s_p = NULL; cfg->i_p = NULL; cfg->s_a = NULL; cfg->i_a = NULL; cfg->s_i = NULL; cfg->i_i = NULL; cfg->uid = (uid_t)0; cfg->gid = (gid_t)0; cfg->port = 0; cfg->ubl = 5; cfg->ibl = 5; $? "- pqdconf_init" } /** Prepare a configuration structure to read a configuration file. @param cfg Configuration to prepare. @param type Type of caller program. @param app Application structure for diagnostics. @param cmsg Localized messages related to configuration. @return 1 on success, 0 on error. */ static int pqdconf_prepare( pqd_conf_t *cfg, int type, dk3_app_t *app, dkChar const * const *cmsg ) { int back = 1; $? "+ pqdconf_prepare %d", type switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { back = 0; cfg->s_c = dk3sto_open_app(app); if(cfg->s_c) { dk3sto_set_comp(cfg->s_c, pqdconf_class_compare, 0); cfg->i_c = dk3sto_it_open(cfg->s_c); if(cfg->i_c) { cfg->s_p = dk3sto_open_app(app); if(cfg->s_p) { dk3sto_set_comp(cfg->s_p, pqdconf_printer_compare, 0); cfg->i_p = dk3sto_it_open(cfg->s_p); if(cfg->i_p) { cfg->s_a = dk3sto_open_app(app); if(cfg->s_a) { dk3sto_set_comp(cfg->s_a, pqdconf_alias_compare, 0); cfg->i_a = dk3sto_it_open(cfg->s_a); if(cfg->i_a) { back = 1; } } } } } } if(!(back)) { /* ERROR: MEMORY */ } } break; case PQD_PROGRAM_TYPE_INFO: { back = 0; cfg->s_i = dk3sto_open_app(app); if(cfg->s_i) { dk3sto_set_comp(cfg->s_i, dk3socket_compare_peer, 0); cfg->i_i = dk3sto_it_open(cfg->s_i); if(cfg->i_i) { back = 1; } } if(!(back)) { /* ERROR: Memory */ } } break; } $? "- pqdconf_prepare %d", back return back; } /** Check whether or not an alias already exists. @param cfg Configuration structure. @param aln Alias name to test. @param app Application structure for diagnostics. @return 1 if alias already exists, 0 if it does not yet exist. */ static int pqdconf_check_alias_name( pqd_conf_t *cfg, char const *aln, dk3_app_t *app ) { int back = 0; $? "+ยด pqdconf_check_alias_name \"%s\"", TR_STR(aln) if(dk3sto_it_find_like(cfg->i_a, (void *)aln, 1)) { back = 1; } $? "- pqdconf_check_alias_name %d", back return back; } /** Check whether or not a printer already exists. @param cfg Configuration structure. @param prn Printer name to test. @param app Application structure for diagnostics. @return 1 if printer already exists, 0 if it does not yet exist. */ static int pqdconf_check_printer_name( pqd_conf_t *cfg, char const *prn, dk3_app_t *app ) { pqd_printer_t *lp; int back = 0; $? "+ pqdconf_check_printer_name \"%s\"", TR_STR(prn) lp = (pqd_printer_t *)dk3sto_it_find_like(cfg->i_p, (void *)prn, 1); if(lp) { $? ". found \"%s\"", lp->name back = 1; } $? "- pqdconf_check_printer_name %d", back return back; } /** Check whether a limit for the specified group was already found. @param i_glim Iterator through group limits storage. @param name Group name to check. @return 1 for found, 0 for not found. */ static int pqdconf_check_group_limit( dk3_sto_it_t *i_glim, char const *name ) { pqd_limit_t *pl; int back = 0; $? "+ pqdconf_check_group_limit \"%s\"", TR_STR(name) dk3sto_it_reset(i_glim); while((NULL != (pl = (pqd_limit_t *)dk3sto_it_next(i_glim))) && (0 == back)) { if(strcmp(pl->name, name) == 0) { back = 1; } } $? "- pqdconf_check_group_limit %d", back return back; } /** Find limit value. @param ulp Pointer to destination variable. @param str Source text. @param app Application structure for diagnostics. @param cmsg Localized messages related to configuration. @return 1 on success, 0 on error. */ static int pqdconf_get_limit_value( unsigned long *ulp, char const *str, dk3_app_t *app, char const * const *cmsg ) { unsigned long ul; int back = 0; $? "+ pqdconf_get_limit_value \"%s\"", TR_STR(str) if(strcmp(pqdconf_c8_kw[1], str) == 0) { *ulp = ul = DK3_UL_MAX; back = 1; } else { if(strcmp(pqdconf_c8_kw[2], str) == 0) { *ulp = ul = 0UL; back = 1; } else { if(1 == sscanf(str, "%lu", &ul)) { *ulp = ul; back = 1; } else { ul = 0UL; /* ERROR: Not a limit value! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 1, 2, str); } } } $? "- pqdconf_get_limit_value %d %lu", back, ul return back; } int pqdconf_read( pqd_conf_t *cfg, char const *fn, int type, dk3_app_t *app, dkChar const * const *cmsg ) { char bu[PQD_CONFIG_BUFFER_SIZE]; /* Line buffer to read file. */ dk3_peer_allowed_t peer; /* Allowed peer. */ dk3_peer_allowed_t *np; /* New peer. */ FILE *fipo; /* Input file. */ char *p1; /* Start of line. */ char *p2; /* Start of value. */ char *p3; /* Second part of value. */ pqd_class_t *pc; /* Current class. */ struct passwd *pw; /* Run as user. */ struct group *gr; /* Run as group. */ pqd_printer_t *pp; /* Current printer. */ pqd_alias_t *pa; /* Current alias. */ pqd_limit_t *pl; /* New limit. */ char const *oldsrcfile; /* Old source file name. */ unsigned long ul; /* Result from sscanf. */ unsigned long oldsrcline; /* Old source file line. */ unsigned long lineno = 0UL; /* Current line number. */ int cc; /* Flag: Can continue. */ int st; /* Section type. */ int act; /* Action to take. */ int back = 0; $? "+ pqdconf_read" oldsrcfile = dk3app_get_source_file(app); oldsrcline = dk3app_get_source_line(app); dk3app_set_source_file(app, fn); dk3app_set_source_line(app, 0UL); pqdconf_init(cfg); if(pqdconf_prepare(cfg, type, app, cmsg)) { $? ". prepare" fipo = dk3sf_fopen_app(fn, pqdconf_c8_kw[0], app); if(fipo) { $? ". fopen" back = 1; st = 0; pp = NULL; pc = NULL; do { cc = 0; if(fgets(bu, sizeof(bu), fipo)) { lineno++; dk3app_set_source_line(app, lineno); cc = 1; dk3str_c8_delnl(bu); $? ". line %lu = \"%s\"", lineno, bu p1 = dk3str_c8_start(bu, NULL); if(p1) { switch(*p1) { case '#': { $? ". comment" /* Comments are always ok. */ } break; case '[': { $? ". new section" pp = NULL; pc = NULL; st = -1; p2 = dk3str_c8_chr(p1, ']'); if(p2) { p1++; *p2 = '\0'; p2 = dk3str_c8_next(p1, NULL); $? ". %s %s", TR_STR(p1), TR_STR(p2) st = dk3str_c8_array_index( pqdconf_section_types, p1, 0 ); switch(st) { case 2: { switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { if(p2) { if(pqdconf_check_alias_name(cfg, p2, app)) { back = 0; $? "! alias exists" /* ERROR: Alias already exists! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 3, 4, p2); } else { pp = (pqd_printer_t *)dk3sto_it_find_like( cfg->i_p, p2, 1 ); if(pp) { back = 0; $? "! printer exists" /* ERROR: Printer already defined! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 5, 6, p2); pp = NULL; } else { pp = pqdconf_printer_new(p2, app); if(pp) { if(!dk3sto_add(cfg->s_p, (void *)pp)) { back = 0; $? "! store printer" pqdconf_printer_delete(pp); pp = NULL; } } else { $? "! allocate printer" back = 0; /* ERROR: Memory */ } } if(!(pp)) { $? "! allocate printer" back = 0; } } } else { back = 0; $? "! printer name missing" /* ERROR: Syntax, argument required! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 38); } } break; } } break; case 1: { switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { if(p2) { pc = (pqd_class_t *)dk3sto_it_find_like( cfg->i_c, p2, 1 ); if(pc) { back = 0; $? "! class exists" /* ERROR: Class already defined! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 7, 8, p2); pc = NULL; } else { pc = pqdconf_class_new(p2, app); if(pc) { if(!dk3sto_add(cfg->s_c, (void *)pc)) { back = 0; $? "! store class" pqdconf_class_delete(pc); pc = NULL; } } else { $? "! allocate class" back = 0; /* ERROR: Memory */ } } if(!(pc)) { $? "! alloate class" back = 0; } } else { back = 0; $? "! missing class name" /* ERROR: Syntax, argument required! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 9); } } break; } } break; case 0: { if(p2) { back = 0; $? "! unused argument" /* ERROR: Syntax, no argument allowed! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 10); } } break; default: { $? "! illegal section name" back = 0; /* ERROR: Illegal configuration section name! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 11, 12, p1); } break; } } else { $? "! closing bracket is missing" /* ERROR: Closing bracket is missing */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 13); } } break; default: { $? ". section contents" p2 = dk3str_c8_chr(p1, '='); if(p2) { *(p2++) = '\0'; p2 = dk3str_c8_start(p2, NULL); if(p2) { dk3str_c8_normalize(p1, NULL, ' '); switch(st) { case 2: { /* Configure current printer. */ if(PQD_PROGRAM_TYPE_DAEMON == type) { if(pp) { $? ". configure printer" act = dk3str_c8_array_index( pqdconf_section_printer, p1, 0 ); switch(act) { case 0: { $? ". set class for printer" if(pp->cl) { $? "! class already configured" back = 0; /* ERROR: Already configured! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 14); } else { pc = (pqd_class_t *)dk3sto_it_find_like( cfg->i_c, p2, 1 ); if(pc) { $? ". class ok" pp->cl = pc; pc = NULL; } else { $? "! no such class" back = 0; /* ERROR: No such class! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,15,16,p2); } } } break; case 1: { $? ". set alias for printer" if(pqdconf_check_printer_name(cfg,p2,app)) { back = 0; $? "! printer name exists" /* ERROR: Printer already exists! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,5,6,p2); } else { if(pqdconf_check_alias_name(cfg,p2,app)) { back = 0; $? "! alias name exists" /* ERROR: Alias already exists! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,3,4,p2); } else { pa = pqdconf_alias_new(p2, pp, app); if(pa) { if(!dk3sto_add(cfg->s_a, (void *)pa)) { back = 0; $? "! store" pqdconf_alias_delete(pa); } } else { $? "! memory" back = 0; /* ERROR: Memory */ } pa = NULL; } } } break; default: { $? "! illegal key" back = 0; /* ERROR: Unknown key! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,17,18,p1); } break; } } } } break; case 1: { /* Configure current class. */ if(PQD_PROGRAM_TYPE_DAEMON == type) { if(pc) { $? ". configure class" act = dk3str_c8_array_index( pqdconf_section_class, p1, 0 ); switch(act) { case 0: { $? ". set default limit" if(pqdconf_get_limit_value(&ul,p2,app,cmsg)) { pc->dl = ul; $? ". limit ok" } else { back = 0; $? "! limit" } } break; case 1: { $? ". add group limit" p3 = dk3str_c8_chr(p2, ':'); if(p3) { *(p3++) = '\0'; if(pqdconf_check_group_limit(pc->i_g,p2)) { back = 0; $? "! group limit exists" /* ERROR: Limit group already defined. */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,19,20,p2); } else { if(pqdconf_get_limit_value(&ul,p3,app,cmsg)) { pl = pqdconf_limit_new(p2, ul, app); if(pl) { $? ". limit" if(!dk3sto_add(pc->s_g, (void *)pl)) { $? "! memory" back = 0; /* ERROR: Memory. */ pqdconf_limit_delete(pl); } } else { $? "! memory" back = 0; /* ERROR: Memory. */ } } else { $? "! limit" back = 0; } } } else { $? "! missing :" back = 0; /* ERROR: Syntax */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 21); } } break; case 2: { $? ". add user limit" p3 = dk3str_c8_chr(p2, ':'); if(p3) { *(p3++) = '\0'; if(dk3sto_it_find_like(pc->i_u,p2,1)) { back = 0; $? "! limit exists" /* ERROR: Limit for user already defined. */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,22,23,p2); } else { if(pqdconf_get_limit_value(&ul,p3,app,cmsg)) { pl = pqdconf_limit_new(p2, ul, app); if(pl) { $? ". limit" if(!dk3sto_add(pc->s_u, (void *)pl)) { $? "! memory" back = 0; /* ERROR: Memory! */ pqdconf_limit_delete(pl); } } else { $? "! memory" back = 0; /* ERROR: Memory! */ } } else { $? "! limit" back = 0; /* ERROR: Syntax! */ } } } else { back = 0; $? "! missing :" /* ERROR: Syntax! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 21); } } break; case 3: { $? ". set deny action" pc->da = dk3str_array_index( pqdconf_deny_actions, p2, 0 ); if(pc->da < 0) { $? "! deny action" back = 0; /* ERROR: Syntax! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,24,25,p2); } } break; default: { $? "! illegal key" back = 0; /* ERROR: Unknown key */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,17,18,p1); } break; } } } } break; case 0: { /* Configure options. */ act = dk3str_c8_array_index( pqdconf_section_options, p1, 0 ); switch(act) { case 0: { /* database */ switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { $? ". db" if(cfg->dbn) { back = 0; $? "! already defined" /* ERROR: Database already defined! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 26); } else { dk3str_c8_normalize(p2, NULL, ' '); cfg->dbn = dk3str_c8_dup_app(p2, app); if(!(cfg->dbn)) { $? "! memory" back = 0; /* ERROR: Memory */ } } } break; } } break; case 1: { /* socket */ switch(type) { case PQD_PROGRAM_TYPE_DAEMON: case PQD_PROGRAM_TYPE_MULTIPLEXOR: case PQD_PROGRAM_TYPE_ADMIN: case PQD_PROGRAM_TYPE_INFO: { $? ". socket" if(cfg->usn) { back = 0; $? "! already defined" /* ERROR: Socket already defined! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 27); } else { dk3str_c8_normalize(p2, NULL, ' '); cfg->usn = dk3str_c8_dup_app(p2, app); if(!(cfg->usn)) { $? "! memory" back = 0; /* ERROR: MEMORY */ } } } break; } } break; case 2: { /* info port */ switch(type) { case PQD_PROGRAM_TYPE_INFO: { $? ". info port" if(cfg->port) { back = 0; $? "! already defined" /* ERROR: Port already defined! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 28); } else { unsigned u; if(1 == sscanf(p2, "%u", &u)) { cfg->port = (unsigned short)u; #if VERSION_BEFORE_20140809 if(u != (unsigned)(cfg->port)) #else if(u > (unsigned)(DK3_US_MAX)) #endif { back = 0; $? "! overflow" /* ERROR: Range overflow! */ dk3app_log_3( app,DK3_LL_ERROR,cmsg,29,30,p2 ); } } else { $? "! port" back = 0; /* ERROR: Not a number! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,30,31,p2); } } } break; } } break; case 3: { /* info allow */ switch(type) { case PQD_PROGRAM_TYPE_INFO: { $? ". info all" if(dk3socket_set_peer(&peer, p2, NULL, app)) { np = pqdconf_peer_new(&peer, app); if(np) { if(!dk3sto_add(cfg->s_i, (void *)np)) { back = 0; $? "! memory" dk3_delete(np); } } else { $? "! memory" back = 0; /* ERROR: Memory */ } } else { $? "! not a valid peer" back = 0; /* ERROR: Not a valid peer! */ } } break; } } break; case 4: { /* run as user */ switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { $? ". run user" if(cfg->uid) { back = 0; $? "! already defined" /* ERROR: Already defined! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 32); } else { pw = getpwnam(p2); if(pw) { cfg->uid = pw->pw_uid; } else { $? "! no such user" back = 0; /* ERROR: No such user! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,34,35,p2); } } } break; } } break; case 5: { /* run as group */ switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { $? ". run group" if(cfg->gid) { back = 0; $? "! already defined" dk3app_log_1(app, DK3_LL_ERROR, cmsg, 33); } else { gr = getgrnam(p2); if(gr) { cfg->gid = gr->gr_gid; } else { $? "! no such group" back = 0; /* ERROR: No such group! */ dk3app_log_3(app,DK3_LL_ERROR,cmsg,36,37,p2); } } } break; } } break; case 6: case 7: { $? ". backlog / info backlog" int i; if(p2) { if(1 == sscanf(p2, "%d", &i)) { if(i > 0) { if(7 == act) { cfg->ibl = i; } else { cfg->ubl = i; } } } } } break; default: { $? "! illegal key" back = 0; /* ERROR: Unknown key! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 17, 18, p1); } break; } } break; } } else { $? "! missing value" back = 0; /* ERROR: Missing value! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 38); } } else { $? "! missing =" back = 0; /* ERROR: Missing value! */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 38); } } break; } } else { $? ". empty line, ok" } $? ". back = %d", back } } while((back == 1) && (cc == 1)); fclose(fipo); } else { $? "! fopen" } } else { $? "! prepare" } dk3app_set_source_file(app, oldsrcfile); dk3app_set_source_line(app, oldsrcline); $? "- pqdconf_read %d", back return back; } void pqdconf_cleanup(pqd_conf_t *cfg) { pqd_class_t *pcl; pqd_printer_t *ppr; pqd_alias_t *pal; dk3_peer_allowed_t *ppeer; $? "+ pqdconf_cleanup" $? ". remove classes" if(cfg->s_c) { if(cfg->i_c) { dk3sto_it_reset(cfg->i_c); while(NULL != (pcl = (pqd_class_t *)dk3sto_it_next(cfg->i_c))) { pqdconf_class_delete(pcl); } dk3sto_it_close(cfg->i_c); } dk3sto_close(cfg->s_c); } cfg->s_c = NULL; cfg->i_c = NULL; $? ". remove printers" if(cfg->s_p) { if(cfg->i_p) { dk3sto_it_reset(cfg->i_p); while(NULL != (ppr = (pqd_printer_t *)dk3sto_it_next(cfg->i_p))) { pqdconf_printer_delete(ppr); } dk3sto_it_close(cfg->i_p); } dk3sto_close(cfg->s_p); } cfg->s_p = NULL; cfg->i_p = NULL; $? ". remove aliases" if(cfg->s_a) { if(cfg->i_a) { dk3sto_it_reset(cfg->i_a); while(NULL != (pal = (pqd_alias_t *)dk3sto_it_next(cfg->i_a))) { pqdconf_alias_delete(pal); } dk3sto_it_close(cfg->i_a); } dk3sto_close(cfg->s_a); } cfg->s_a = NULL; cfg->i_a = NULL; $? ". remove allowed information peers" if(cfg->s_i) { if(cfg->i_i) { dk3sto_it_reset(cfg->i_i); while(NULL != (ppeer=(dk3_peer_allowed_t *)dk3sto_it_next(cfg->i_i))) { pqdconf_peer_delete(ppeer); } dk3sto_it_close(cfg->i_i); } dk3sto_close(cfg->s_i); } cfg->s_i = NULL; cfg->i_i = NULL; $? ". remove socket file name" if(cfg->usn) { $? ". socket \"%s\"", TR_STR(cfg->usn) dk3_release(cfg->usn); } cfg->usn = NULL; $? ". remove database file name" if(cfg->dbn) { $? ". db \"%s\"", TR_STR(cfg->dbn) dk3_release(cfg->dbn); } cfg->dbn = NULL; cfg->port = 0; $? "- pqdconf_cleanup" } int pqdconf_check( pqd_conf_t *cfg, int type, dk3_app_t *app, dkChar const * const *cmsg ) { char bu[DK3_MAX_PATH]; char const *p1; int back = 0; $? "+ pqdconf_check" if(cfg->ubl < 5) { cfg->ubl = 5; } if(cfg->ibl < 5) { cfg->ibl = 5; } if(!(cfg->usn)) { p1 = dk3inst_get_directory(3); if(p1) { if(strlen(p1) < sizeof(bu)) { strcpy(bu, p1); p1 = pqdconf_c8_kw[3]; if((strlen(bu) + strlen(p1)) < sizeof(bu)) { strcat(bu, p1); cfg->usn = dk3str_c8_dup_app(bu, app); } else { /* ERROR: Local state directory too long! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 39, 40, p1); } } else { /* ERROR: Local state directory too long! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 39, 40, p1); } } else { /* ERROR: Failed to find local state directory */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 41); } } if(PQD_PROGRAM_TYPE_DAEMON == type) { if(!(cfg->dbn)) { p1 = pqdconf_c8_kw[4]; if(dk3dbi_type_supported(DK3_DB_TYPE_NDBM)) { p1 = pqdconf_c8_kw[5]; } if(dk3dbi_type_supported(DK3_DB_TYPE_BDB)) { p1 = pqdconf_c8_kw[6]; } strcpy(bu, p1); p1 = dk3inst_get_directory(3); if(p1) { if((strlen(bu) + strlen(p1)) < sizeof(bu)) { strcat(bu, p1); p1 = pqdconf_c8_kw[7]; if((strlen(bu) + strlen(p1)) < sizeof(bu)) { strcat(bu, p1); cfg->dbn = dk3str_c8_dup_app(bu, app); } else { /* ERROR: Local state directory too long! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 39, 40, p1); } } else { /* ERROR: Local state directory too long! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 39, 40, p1); } } else { /* ERROR: Failed to find local state directory */ dk3app_log_1(app, DK3_LL_ERROR, cmsg, 41); } } } if(cfg->usn) { if(strlen(cfg->usn) < 108) { back = 1; switch(type) { case PQD_PROGRAM_TYPE_DAEMON: { back = 0; if(cfg->dbn) { back = 1; } } break; case PQD_PROGRAM_TYPE_INFO: { if(!(cfg->port)) { cfg->port = PRINTQD_DEFAULT_PORT; } } break; } } else { /* ERROR: Socket name too long! */ dk3app_log_3(app, DK3_LL_ERROR, cmsg, 42, 43, cfg->usn); } } $? "- pqdconf_check %d", back return back; } #endif #endif #endif #endif