1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2016-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7
8
9%%	header
10
11/* CORRECT NEXT LINE AFTER REPLACING PRINTQD */
12
13/**	@file	printqd.h	Definitions for printqd.
14*/
15
16#ifndef DK4CONF_H_INCLUDED
17#include "dk4conf.h"
18#endif
19
20#ifndef	DK4TYPES_H_INCLUDED
21#include <libdk4base/dk4types.h>
22#endif
23
24#ifndef	DK4STO_H_INCLUDED
25#include <libdk4c/dk4sto.h>
26#endif
27
28#ifndef	DK4SOCK_H_INCLUDED
29#include <libdk4sock/dk4sock.h>
30#endif
31
32
33
34/**	Limit for one user or group.
35*/
36typedef struct {
37  char		*name;	/**< User or group name */
38  dk4_um_t	 limit;	/**< Page limit. */
39} pqd_limit_t;
40
41
42
43
44/**	Printer class (may contain multiple printers).
45*/
46typedef struct {
47  char		*name;	/**< Class name. */
48  dk4_sto_t	*s_u;	/**< User limits storage. */
49  dk4_sto_it_t	*i_u;	/**< User limits storage iterator. */
50  dk4_sto_t	*s_g;	/**< Group limits storage. */
51  dk4_sto_it_t	*i_g;	/**< Group limits storage iterator. */
52  dk4_um_t	 dl;	/**< Default limit. */
53  int		 da;	/**< Deny action: 1=hold, 0=remove. */
54  int		 hdl;	/**< Flag: Have default limit. */
55} pqd_class_t;
56
57
58
59/**	Printer.
60*/
61typedef struct {
62  char		*name;	/**< Printer name. */
63  pqd_class_t	*cl;	/**< Printer class. */
64} pqd_printer_t;
65/*
66	In service the class is not NULL.
67	If no class is configured for a printer, the program complains
68	and exits.
69*/
70
71
72
73/**	Printer alias.
74*/
75typedef struct {
76  char		*name;	/**< Alias name. */
77  pqd_printer_t	*pr;	/**< The real printer. */
78} pqd_printer_alias_t;
79
80
81
82/**	Printqd configuration.
83*/
84typedef struct {
85  char			*sname;	/**< UNIX domain socket file name. */
86  char			*dname;	/**< Database name (including type). */
87  char			*lname;	/**< Log file name. */
88  dk4_sto_t		*s_c;	/**< Storage for classes. */
89  dk4_sto_it_t		*i_c;	/**< Iterator for class storage. */
90  dk4_sto_t		*s_p;	/**< Storage for printers. */
91  dk4_sto_it_t		*i_p;	/**< Iterator for printers storage. */
92  dk4_sto_t		*s_a;	/**< Storage for aliases. */
93  dk4_sto_it_t		*i_a;	/**< Iterator for aliases storage. */
94  dk4_sto_t		*s_ai;	/**< Storage for peers allowed info rq. */
95  dk4_sto_it_t		*i_ai;	/**< Iterator for info peers storage. */
96  dk4_sto_t		*s_ad;	/**< Storage for peers allowed data rq. */
97  dk4_sto_it_t		*i_ad;	/**< Iterator for data peers storage. */
98  dk4_sto_t		*s_aa;	/**< Storage for peers allowed admin rq. */
99  dk4_sto_it_t		*i_aa;	/**< Iterator for admin peers storage. */
100  size_t		 m_loc;	/**< Maximum number of local connections. */
101  size_t		 m_tcp;	/**< Maximum number of TCP connections. */
102  uid_t			 uid;	/**< Run as specified user. */
103  uid_t			 suid;	/**< Local socket owner UID. */
104  gid_t			 gid;	/**< Run as specified group. */
105  gid_t			 sgid;	/**< Local socket owner GID. */
106  int			 slbl;	/**< Backlog for local socket. */
107  int			 snbl;	/**< Backlog for network socket. */
108  int			 iglo;	/**< Flag: Globally allow info requests. */
109  int			 dglo;	/**< Flag: Globally allow data requests. */
110  int			 aglo;	/**< Flag: Globally allow admin requests. */
111  int			 linf;	/**< Flag: Log info requests. */
112  int			 ldb;	/**< Flag: Log database modifications. */
113  unsigned short	 ptcp;	/**< TCP port (info, data, admin), host repr. */
114  unsigned short	 pudp;	/**< UDP port (info only), host represent. */
115} pqd_conf_t;
116
117
118
119/**	Connection record for local socket.
120*/
121typedef struct {
122  dk4_socket_t		sock;	/**< Socket. */
123} pqd_l_conn_t;
124
125
126
127/**	Connection record for network socket.
128*/
129typedef struct {
130  dk4_sockaddr_storage_t	raddr;	/**< Remote address. */
131  dk4_socket_t			sock;	/**< Socket. */
132  int				pqdpl;	/**< Printqd protocol level. */
133} pqd_n_conn_t;
134
135
136
137/**	One request to process.
138*/
139typedef struct {
140  void			*connptr;	/**< Connection record for socket. */
141  dk4_sto_t		*sto;		/**< Container for conn records. */
142  struct sockaddr	*soa;		/**< Remote address, only UDP. */
143  size_t		*pnconn;	/**< Addr of variable, NULL for UDP. */
144  char			*cmdptr;	/**< Start of command. */
145  char			*argptr;	/**< Command arguments. */
146  size_t		 szsoa;		/**< Size of rem address, only UDP. */
147  dk4_socket_t		 sock;		/**< Socket to use for response. */
148  int			 protlev;	/**< Protocol level. */
149  int			 action;	/**< Action to take. */
150  int			 logthis;	/**< Flag: Log request and response. */
151} pqd_rq_t;
152
153
154
155/**	Data for one user in a class.
156*/
157typedef struct {
158  dk4_um_t	limit;		/**< Limit (maximum page number). */
159  dk4_um_t	used;		/**< Number of pages used within limit. */
160  dk4_um_t	account;	/**< Remaining pages personal print account. */
161  int		da;		/**< Deny action: 0=remove, 1=hold. */
162} pqd_account_t;
163
164
165
166/**	Information from one LPRng accounting line.
167*/
168typedef struct {
169  char		*username;	/**< User name. */
170  char		*queuename;	/**< Print queue name. */
171  char		*jobtitle;	/**< Print job title. */
172  char		*pagecount;	/**< Pagecount reported in line. */
173} lprng_info_t;
174
175
176
177
178/**	Printqd protocol levels.
179*/
180enum {
181  PQD_PROTO_NONE	= 0 ,	/**< No requests allowed. */
182  PQD_PROTO_INFO	= 1 ,	/**< Info requests only. */
183  PQD_PROTO_DATA	= 2 ,	/**< Info and data requests. */
184  PQD_PROTO_ADMIN	= 3	/**< Info, data and admin requests. */
185};
186
187
188
189/**	Commands in the printqd protocol.
190*/
191enum {
192  PQD_CMD_NONE		= -1 ,	/**< Illegal command. */
193  PQD_CMD_JOBSTART	=  0 ,	/**< Start of print job, response required. */
194  PQD_CMD_JOBEND	=  1 ,	/**< End of print job. */
195  PQD_CMD_START		=  2 ,	/**< Start of printing (ignored). */
196  PQD_CMD_END		=  3 ,	/**< End of printing (ignored). */
197  PQD_CMD_FILESTART	=  4 ,	/**< Start of file printing (pc). */
198  PQD_CMD_FILEEND	=  5 ,	/**< End of file printing (pc). */
199  PQD_CMD_INFO		=  6 ,	/**< Information, response required. */
200  PQD_CMD_ACCT_CHECK	=  7 ,	/**< Check permission, response required. */
201  PQD_CMD_ACCT_START	=  8 ,	/**< Start of file printing (pc). */
202  PQD_CMD_ACCT_END	=  9 ,	/**< End of file printing (pc). */
203  PQD_CMD_ACCT_CHARGE	= 10 ,	/**< Summary for file printing (pc). */
204  PQD_CMD_CONTROL	= 11	/**< Control request. */
205};
206
207
208
209%%	module
210
211/**	@file	printqd.c	The printqd daemon.
212
213A print accounting and quota daemon for use with the LPRng print system.
214*/
215
216
217
218#include "dk4conf.h"
219#include <libdk4base/dk4types.h>
220
221
222
223/* CORRECT NEXT LINE AFTER REPLACING PRINTQD */
224#include <printqd/printqd.h>
225
226
227
228#if	(DK4_CHAR_SIZE == 1) \
229&&	((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) \
230&&	DK4_HAVE_GETPWNAM && DK4_HAVE_GETGRNAM \
231&&	((DK4_HAVE_SETSID) || (DK4_HAVE_SETPGRP)) \
232&&	((DK4_HAVE_PWD_H) && (DK4_HAVE_GRP_H)) \
233&&	DK4_HAVE_UID_T && DK4_HAVE_GID_T && DK4_HAVE_MODE_T && (!DK4_ON_WINDOWS)
234
235
236
237#include <stdio.h>
238
239#if	DK4_HAVE_SYS_TYPES_H
240#ifndef	SYS_TYPES_H_INCLUDED
241#include <sys/types.h>
242#define	SYS_TYPES_H_INCLUDED 1
243#endif
244#endif
245
246#if	DK4_HAVE_SYS_STAT_H
247#ifndef	SYS_STAT_H_INCLUDED
248#include <sys/stat.h>
249#define	SYS_STAT_H_INCLUDED 1
250#endif
251#endif
252
253#if	DK4_HAVE_FCNTL_H
254#ifndef	FCNTL_H_INCLUDED
255#include <fcntl.h>
256#define	FCNTL_H_INCLUDED 1
257#endif
258#endif
259
260#if	DK4_HAVE_ERRNO_H
261#ifndef	ERRNO_H_INCLUDED
262#include <errno.h>
263#define	ERRNO_H_INCLUDED 1
264#endif
265#endif
266
267#if	DK4_HAVE_UNISTD_H
268#ifndef	UNISTD_H_INCLUDED
269#include <unistd.h>
270#define	UNISTD_H_INCLUDED 1
271#endif
272#endif
273
274#if	DK4_HAVE_PWD_H
275#ifndef	PWD_H_INCLUDED
276#include <pwd.h>
277#define	PWD_H_INCLUDED 1
278#endif
279#endif
280
281#if	DK4_HAVE_GRP_H
282#ifndef	GRP_H_INCLUDED
283#include <grp.h>
284#define	GRP_H_INCLUDED 1
285#endif
286#endif
287
288#if	DK4_HAVE_SYSLOG_H
289#ifndef	SYSLOG_H_INCLUDED
290#include <syslog.h>
291#define	SYSLOG_H_INCLUDED 1
292#endif
293#endif
294
295#if	DK4_HAVE_SYSEXITS_H
296#ifndef	SYSEXITS_H_INCLUDED
297#include <sysexits.h>
298#define	SYSEXITS_H_INCLUDED 1
299#endif
300#endif
301
302#include <libdk4c/dk4opt.h>
303#include <libdk4c/dk4dmt.h>
304#include <libdk4c/dk4inst.h>
305#include <libdk4c/dk4dmt.h>
306#include <libdk4c/dk4sto.h>
307#include <libdk4sock/dk4sock.h>
308#include <libdk4base/dk4mem.h>
309#include <libdk4base/dk4str8.h>
310#include <libdk4maio8d/dk4mai8dii.h>
311#include <libdk4maio8d/dk4mai8dsz.h>
312#include <libdk4maio8d/dk4mai8dus.h>
313#include <libdk4maio8d/dk4mai8ddu.h>
314#include <libdk4maio8d/dk4mao8d.h>
315#include <libdk4c/dk4fopc8.h>
316#include <libdk4dbi/dk4dbi.h>
317#include <libdk4dbi/dk4dbit8.h>
318#include <libdk4c/dk4time.h>
319#include <libdk4c/dk4time08.h>
320#include <libdk4c/dk4mkdh8.h>
321#include <libdk4c/dk4delfile08.h>
322#include <libdk4base/dk4unused.h>
323
324
325
326$!trace-include
327
328
329
330#ifndef	LOG_LPR
331/**	Default log feature for printing.
332*/
333#define	LOG_LPR (6 << 3)
334#endif
335
336
337
338#ifndef	LOG_EMERG
339/**	Log message level to ignore, printqd does not make system unusable.
340*/
341#define	LOG_EMERG	(0)
342#endif
343
344#ifndef	LOG_ERR
345/**	Log message level error.
346*/
347#define	LOG_ERR		(3)
348#endif
349
350#ifndef	LOG_INFO
351/**	Log message level info.
352*/
353#define	LOG_INFO	(6)
354#endif
355
356/**	Input buffer, used
357	* for configuration file reading and
358	* for reading requests.
359*/
360static	char		inbuf[1024];
361
362
363/**	Buffer to construct key for database entries.
364*/
365static	char		kb[sizeof(inbuf)];
366
367
368/**	Buffer to construct/convert values for database entries.
369*/
370static	char		vb[sizeof(inbuf)];
371
372
373/**	Options used by the program.
374*/
375static	dk4_option_t	options[] = {
376
377  /*	Debug mode.
378  */
379  { { dkT('d'),	dkT("debug"),	DK4_OPT_ARG_NONE },	{ NULL },	0 },
380
381  /*	Configuration file.
382  */
383  { { dkT('c'),	dkT("config"),	DK4_OPT_ARG_STRING },	{ NULL },	0 }
384};
385
386
387
388/**	Default configuration file name.
389*/
390static	const char	def_conf_file_name[] = {
391  DK4_INST_DIR_ETC "/printqd/printqd.conf"
392};
393
394
395
396/**	Default socket file name.
397*/
398static	const char	def_sock_file_name[] = {
399  DK4_INST_DIR_RUNSTATE "/printqd/printqd.sock"
400};
401
402
403
404/**	Default database file name.
405*/
406static	const char	def_db_file_name[] = {
407#if DK4_HAVE_DB_H
408  "bdb::"	DK4_INST_DIR_VAR	"/lib/printqd/printqd.db"
409#else
410#if DK4_HAVE_NDBM_H
411  "ndbm::"	DK4_INST_DIR_VAR	"/lib/printqd/printqd"
412#else
413  "mem::"	DK4_INST_DIR_VAR	"/lib/printqd/printqd.db"
414#endif
415#endif
416};
417
418
419
420/**	Default log file name.
421*/
422static	const char	def_log_file_name[] = {
423  DK4_INST_DIR_VAR "/log/printqd/printqd.log"
424};
425
426
427
428
429/**	PID file name.
430*/
431static	const char	pid_file_name[] = {
432  DK4_INST_DIR_SYSTEMD_RUN "/printqd/printqd.pid"
433};
434
435
436
437/**	Constant texts used by the module.
438*/
439static	const char * const	printqd_kw[] = {
440$!string-table
441#
442#	0	Program name
443#
444printqd
445#
446#	1	File open mode
447#
448r
449#
450#	2	Asterisk
451#
452*
453#
454#	3	unlimited
455#
456unlimited
457#
458#	4	denied
459#
460denied
461#
462#	5	Newline
463#
464\n
465#
466#	6	Start of comment
467#
468\#
469#
470#	7 8
471#
472:
473:
474#
475#	9
476#
477ERROR:
478#
479#	10	File open mode
480#
481a
482#
483#	11	Empty string
484#
485
486#
487#	12 13 14
488#
489Not an integer number:\n"
490", reading stoppend at "
491"!
492#
493#	15
494#
495Positive number required!
496#
497#	16 17 18
498#
499Not a size specification:\n"
500", reading stopped at "
501"!
502#
503#	19 20 21
504#
505Not a 16 bit unsigned number:\n"
506", reading stopped at "
507"!
508#
509#	22
510#
511Port number must not be 0!
512#
513#	23 24
514#
515Too much text, unexpected:\n"
516"!
517#
518#	25 26
519#
520Not a peer definition:\n"
521"!
522#
523#	27
524#
525Not enough memory (allocation failed)!
526#
527#	28 29 30
528#
529Not an unsigned number:\n"
530", reading stopped at "
531"!
532#
533#	31
534#
535Missing limit text!
536#
537#	32 33
538#
539Limit redefinition for: "
540"!
541#
542#	34
543#
544Redefinition of default limit!
545#
546#	35
547#
548Redefinition of deny action!
549#
550#	36 37
551#
552Printer already exists: "
553"!
554#
555#	38 39
556#
557Printer alias already exists: "
558"!
559#
560#	40 41
561#
562Class not found: "
563"!
564#
565#	42
566#
567Redefinition of printers class!
568#
569#	43 44
570#
571Class already exists: "
572"!
573#
574#	45
575#
576Missing class name!
577#
578#	46 47
579#
580Printer or alias already exists: "
581"!
582#
583#	48
584#
585Missing printer name!
586#
587#	49 50
588#
589Illegal section type name: "
590"!
591#
592#	51
593#
594Missing section type!
595#
596#	52
597#
598Redefinition of user ID to run as!
599#
600#	53 54
601#
602User not found: "
603"!
604#
605#	55
606#
607Only allowed in options section!
608#
609#	56
610#
611Redefinition of group ID to run as!
612#
613#	57 58
614#
615Group not found: "
616"!
617#
618#	59
619#
620Redefinition of database name!
621#
622#	60
623#
624Redefinition of socket name!
625#
626#	61
627#
628Only allowed in class section!
629#
630#	62
631#
632Only allowed in printer section!
633#
634#	63
635#
636Redefinition of log file name!
637#
638#	64
639#
640Value missing!
641#
642#	65
643#
644Failed to open file for reading!
645#
646#	66
647#
648User to run as was changed!
649#
650#	67
651#
652Group to run as was changed!
653#
654#	68
655#
656Failed to install signal handlers!
657#
658#	69
659#
660Failed to restore signal handlers!
661#
662#	70
663#
664: ERROR: Failed to create new process!\n
665#
666#	71
667#
668Failed to create new process!
669#
670#	72
671#
672Service start prevented by errors!
673#
674#	73 74
675#
676Daemon starting.
677Daemon exited.
678#
679#	75 76 77 78
680#
681Entering service mode.
682Exiting service mode.
683Exiting service mode for reconfiguration.
684Exited service mode.
685#
686#	79
687#
688o:test
689#
690#	80 81 82 83 84	Response keywords
691#
692ACCEPT
693REMOVE
694HOLD
6950 0 0 0
696-1
697#
698#	85		Space
699#
700
701#
702#	86 87		Markers for request and response
703#
704<-
705->
706#
707#	88 89 90	Start of db keys
708#
709p:
710a:
711j:
712#
713#	91 92		Error: No class for printer
714#
715No class configured for printer: "
716"!
717#
718#	93 94
719#
720Warning: No default limit configured for class: "
721"!
722#
723#	95 96
724#
725Failed to create directory hierarchy for log file:\n"
726"!
727#
728#	97 98
729#
730Failed to create directory hierarchy for database:\n"
731"!
732#
733#	99 100
734#
735Failed to create directory hierarchy for socket:\n"
736"!
737#
738#	101 102
739#
740Failed to listen on local socket:\n"
741"!
742#
743#	103
744#
745Failed to create TCP listener socket set!
746#
747#	104
748#
749Failed to create UDP socket set!
750#
751#	105 106
752#
753Failed to open database:\n"
754"!
755#
756#	107
757#
758Failed to write initial entry to database!
759#
760#	108
761#
762Failed to synchronize database to disk!
763#
764#	109
765#
766Failed to change log file ownership!
767#
768#	110
769#
770Failed to change log file permissions!
771#
772#	111
773#
774Failed to change database ownership and/or permissions!
775#
776#	112
777#
778Failed to change local socket ownership!
779#
780#	113
781#
782Failed to change local socket permissions!
783#
784#	114
785#
786Failed to switch group!
787#
788#	115
789#
790Failed to switch user!
791#
792#	116
793#
794Response text too long (bug)!
795#
796#	117
797#
798Failed to write database entry!
799#
800#	118
801#
802Failed to delete database entry!
803#
804#	119
805#
806The select() function failed!
807#
808#	120
809#
810Insufficient memory!
811#
812#	121
813#
814Failed to synchronize database to disk!
815#
816#	122 123 124
817#
818DB SET "
819"="
820" ok.
821#
822# 125 ... 132
823#
824" FAILED (invalid arguments)!
825" FAILED (syntax)!
826" FAILED (numeric overflow in size calculation)!
827" FAILED (unsupported db backend)!
828" FAILED (no such entry)!
829" FAILED (insufficient memory)!
830" FAILED (can not write)!
831" FAILED (reason unknown)!
832#
833#	133
834#
835DB GET "
836#
837#	134
838#
839" FAILED (buffer too small)!
840#
841#	135
842#
843DB DEL "
844#
845#	136 ... 144
846#
847DB SYN ok.
848DB SYN FAILED (invalid arguments)!
849DB SYN FAILED (unsupported db backend)!
850DB SYN FAILED (syntax)!
851DB SYN FAILED (security checks for file failed)!
852DB SYN FAILED (can not open file for writing)!
853DB SYN FAILED (can not write)!
854DB SYN FAILED (can not close file)!
855DB SYN FAILED (unknown reason)!
856#
857#	145 ... 151
858#
859(l=
860,u=
861,a=
862) + u=
863 => (l=
864)
865unlimited
866#
867#	152
868#
869p
870#
871#	153
872#
873) + a=
874#
875#	154 155
876#
877Illegal log feature name: "
878"!
879#
880#	156 157 158
881#
882Failed to update account data for user "
883" in class "
884"!
885#
886#	159 160
887#
888Missing class name in reset request!
889Missing user name in reset request!
890#
891#	161 162 163
892#
893Failed to update account data for user "
894" in class "
895"!
896#
897#	164 165
898#
899No such class: "
900"!
901#
902#	166 167
903#
904Not a number: "
905"!
906#
907#	168 169
908#
909Illegal sub-command: "
910"!
911#
912#	170 171
913#
914Logging to file "
915" failed!
916#
917#	172
918#
919Database modification failed!
920#
921#	173
922#
923Database synchronization to disk failed!
924#
925#	174 175
926#
927Failed to change ownership on database file:\n"
928"!
929#
930#	176 177
931#
932Failed to change permissions on database file:\n"
933"!
934#
935#	178
936#
937" (name too long)!
938#
939#	179
940#
941" (file exists, but no socket)!
942#
943#	180
944#
945" (failed to remove old socket)!
946#
947#	181 182
948#
949" (failed to create socket:
950)!
951#
952#	183
953#
954" (failed to bind local
955#
956#	184
957#
958" (failed to listen
959#
960#	185 186
961#
962" (failed to change ownership)!
963" (failed to change permissions)!
964#
965#	187
966#
967Daemon exited due to errors, see log file!
968#
969#	188 189
970#
971Failed to create TCP socket set (math overflow)!
972Failed to create TCP socket set (memory)!
973#
974#	190 191 192 193 194
975#
976Failed to create TCP socket set (socket():
977)!
978Failed to create TCP socket set (bind():
979Failed to create TCP socket set (listen():
980Failed to create TCP socket set (getaddrinfo():
981#
982#	195 196
983#
984Failed to create UDP socket set (math overflow)!
985Failed to create UDP socket set (memory)!
986#
987#	197 ... 201
988#
989Failed to create UDP socket set (socket():
990)!
991Failed to create UDP socket set (bind():
992Failed to create UDP socket set (listen():
993Failed to create UDP socket set (getaddrinfo():
994#
995#	202 ... 209
996#
997" (backend type not supported)!
998" (overflow in size calculation)!
999" (insufficient memory)!
1000"\n(not a regular file / not a database / key or value size too large)!
1001" (denied by security check)!
1002" (failed to open for reading)!
1003" (failed to write to file)!
1004" (failed to synchronize to disk)!
1005#
1006#	210 ... 216
1007#
1008Failed to write initial entry to database (key or value too long)!
1009Failed to write initial entry to database (unsupported backend type)!
1010Failed to write initial entry to database (insufficient memory)!
1011Failed to write initial entry to database (can not open file for writing)!
1012Failed to write initial entry to database (can not write to file)!
1013Failed to write initial entry to database (can not synchronize to disk)!
1014Failed to write initial entry to database (denied by security check)!
1015#
1016#	217 218
1017#
1018Failed to change PID file ownership!
1019Failed to change PID file permissions!
1020$!end
1021};
1022
1023
1024
1025/**	Choices for deny action.
1026*/
1027static	const char * const	deny_action_names[] = {
1028$!string-table
1029remove
1030hold
1031$!end
1032};
1033
1034
1035
1036/**	Section names in configuration files.
1037*/
1038static	const char * const	config_section_names[] = {
1039$!string-table
1040options
1041class
1042printer
1043$!end
1044};
1045
1046
1047
1048/**	Option names in configuration file.
1049*/
1050static	const char * const	config_option_names[] = {
1051$!string-table
1052#
1053#	0 1
1054#
1055run as user
1056run as group
1057#
1058#	2
1059#
1060database
1061#
1062#	3 4 5
1063#
1064local socket
1065local socket backlog
1066max local connections
1067#
1068#	6
1069#
1070udp port
1071#
1072#	7 8 9
1073#
1074tcp port
1075tcp port backlog
1076max tcp connections
1077#
1078#	10 11 12
1079#
1080info allow
1081data allow
1082admin allow
1083#
1084#	13 14 15
1085#
1086user limit
1087group limit
1088default limit
1089#
1090#	16
1091#
1092deny action
1093#
1094#	17 18
1095#
1096alias
1097class
1098#
1099#	19 20
1100#
1101log file
1102log features
1103#
1104#	21 22
1105#
1106local socket owner
1107local socket group
1108$!end
1109};
1110
1111
1112
1113/**	Command names in the printqd protocol.
1114*/
1115static	const char * const	pqd_command_names[] = {
1116$!string-table
1117jobstart
1118jobend
1119start
1120end
1121filestart
1122fileend
1123info
1124acct-check
1125acct-start
1126acct-end
1127acct-charge
1128control
1129$!end
1130};
1131
1132
1133
1134/**	Sub-commands of control.
1135*/
1136static	const char * const	control_sub_cmds[] = {
1137$!string-table
1138r$eset
1139a$dd
1140d$atabase-cleanup
1141$!end
1142};
1143
1144
1145
1146/**	Argument names for control requests.
1147*/
1148static	const char * const	control_arg_names[] = {
1149$!string-table
1150c$lass
1151u$ser
1152p$ages
1153$!end
1154};
1155
1156
1157
1158/**	Log feature names.
1159*/
1160static const char * const	log_feature_names[] = {
1161$!string-table
1162db
1163info
1164all
1165*
1166none
1167-
1168$!end
1169};
1170
1171
1172
1173static	const char * const	db_type_names[] = {
1174$!string-table
1175p
1176a
1177j
1178$!end
1179};
1180
1181
1182/**	Daemon configuration.
1183*/
1184static	pqd_conf_t	 conf;
1185
1186
1187/**	Daemon tool function structure.
1188*/
1189static	dk4dmt_t	dmt;
1190
1191
1192/**	Connections over local socket, stores pqd_l_conn_t.
1193	Not NULL in service (service is not run if allocation fails).
1194*/
1195static	dk4_sto_t	*s_c_unix	= NULL;
1196
1197
1198
1199/**	Iterator through s_c_unix container.
1200	Not NULL in service (service is not run if allocation failes).
1201*/
1202static	dk4_sto_it_t	*i_c_unix	= NULL;
1203
1204
1205
1206/**	Container for TCP connections, stores pqd_n_conn_t.
1207	Not NULL in service (service is not run if allocation fails).
1208*/
1209static	dk4_sto_t	*s_c_tcp	= NULL;
1210
1211
1212
1213/**	Iterator through s_c_tcp container.
1214	Not NULL in service (service is not run if allocation fails).
1215*/
1216static	dk4_sto_it_t	*i_c_tcp	= NULL;
1217
1218
1219
1220/**	To accept TCP connections.
1221	May be NULL in service, if no TCP port number is conigured.
1222*/
1223static	dk4_socket_set_t	*ss_tcp	= NULL;
1224
1225
1226
1227/**	For info requests.
1228	May be NULL in service, if no UDP port number is configured.
1229*/
1230static	dk4_socket_set_t	*ss_udp	= NULL;
1231
1232
1233
1234/**	Database.
1235	Not NULL in service (service is not run if allocation fails).
1236*/
1237static	dk4_dbi_t		*db	= NULL;
1238
1239
1240
1241/**	Number of options in the options array.
1242*/
1243static const size_t	szoptions =	sizeof(options)/sizeof(dk4_option_t);
1244
1245
1246
1247/**	Current connections on local socket.
1248*/
1249static	size_t		 con_loc	= 0;
1250
1251
1252
1253/**	Current connections on TCP socket.
1254*/
1255static	size_t		 con_tcp	= 0;
1256
1257
1258
1259/**	Timestamp of previous log message.
1260*/
1261static	dk4_time_t	 prev_log_time	= (dk4_time_t)0UL;
1262
1263
1264
1265/**	Listen for incoming UNIX connection requests.
1266	Always set in service, service exits when failed to
1267	bind local address.
1268*/
1269static	dk4_socket_t	 so_unix	= INVALID_SOCKET;
1270
1271
1272
1273/**	Program exit code, either EXIT_FAILURE or EXIT_SUCCESS.
1274*/
1275static	int		 exval	= EXIT_FAILURE;
1276
1277
1278
1279/**	Flag: can continue outer (and inner) loop, 1=yes, 0=no, -1=abort.
1280*/
1281static	int		 ccouter	= 1;
1282
1283
1284
1285/**	Flag: can continue inner loop, 1=yes, 0=no, -1=abort.
1286*/
1287static	int		 ccinner	= 1;
1288
1289
1290
1291/**	Flag: User and group switching was already done.
1292*/
1293static	int		 user_switched	= 0;
1294
1295
1296
1297/**	Syslog feature to use.
1298*/
1299static	int		syslogf	= LOG_LPR;
1300
1301
1302/**	Flag: Modified since last synchronization.
1303*/
1304static	int		db_modified	= 0;
1305
1306
1307/**	Flag: Debug mode, process runs in foreground and logs to stderr.
1308*/
1309static	int		 debug	= 0;
1310
1311
1312/**	Flag: Previous attempt to log to file failed.
1313*/
1314static	int	 	prev_filelog_failed	= 0;
1315
1316
1317/**	Flag: Previous database modification failed.
1318*/
1319static	int		prev_dbmod_failed	= 0;
1320
1321
1322/**	Flag: Previous database synchronization to disk failed.
1323*/
1324static	int		prev_dbsync_failed	= 0;
1325
1326
1327/**	UID in previous pass.
1328*/
1329static	uid_t		 prev_uid	= (uid_t)0;
1330
1331
1332
1333/**	GID in previous pass.
1334*/
1335static	gid_t		 prev_gid	= (gid_t)0;
1336
1337
1338
1339#ifdef SIGPIPE
1340/**	Indicator: SIGPIPE signal received.
1341*/
1342static
1343DK4_VOLATILE
1344dk4_sig_atomic_t	sig_had_pipe	=	0;
1345#endif
1346
1347#ifdef SIGHUP
1348/**	Indicator: SIGHUP signal received.
1349*/
1350static
1351DK4_VOLATILE
1352dk4_sig_atomic_t	sig_had_hup	=	0;
1353#endif
1354
1355
1356/**	Indicator: SIGINT signal received.
1357*/
1358static
1359DK4_VOLATILE
1360dk4_sig_atomic_t	sig_had_int	=	0;
1361
1362
1363/**	Indicator: SIGTERM signal received.
1364*/
1365static
1366DK4_VOLATILE
1367dk4_sig_atomic_t	sig_had_term	=	0;
1368
1369
1370
1371/**	Pass pointer through.
1372	Use of this function is recommended by CERT C coding standard
1373	to avoid optimizing out.
1374	@param	ptr	Pointer to pass through.
1375	@return	The ptr argument.
1376*/
1377static
1378DK4_VOLATILE
1379dk4_sig_atomic_t *
1380sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr)
1381{
1382  return ptr;
1383}
1384
1385
1386#ifdef SIGPIPE
1387/**	Handler for SIGPIPE signal.
1388	@param	signo	Signal number (always SIGPIPE, ignored).
1389*/
1390static
1391void
1392sig_handler_pipe(int DK4_ARG_UNUSED(signo) )
1393{
1394  DK4_UNUSED_ARG(signo)
1395  *sig_pass_pointer(&sig_had_pipe) = 1;
1396}
1397#endif
1398
1399
1400
1401#ifdef SIGHUP
1402/**	Handler for SIGHUP signal.
1403	@param	signo	Signal number (always SIGHUP, ignored).
1404*/
1405static
1406void
1407sig_handler_hup(int DK4_ARG_UNUSED(signo) )
1408{
1409  DK4_UNUSED_ARG(signo)
1410  *sig_pass_pointer(&sig_had_hup) = 1;
1411}
1412#endif
1413
1414
1415
1416/**	Handler for SIGINT signal.
1417	@param	signo	Signal number (always SIGINT, ignored).
1418*/
1419static
1420void
1421sig_handler_int(int DK4_ARG_UNUSED(signo) )
1422{
1423  DK4_UNUSED_ARG(signo)
1424  *sig_pass_pointer(&sig_had_int) = 1;
1425}
1426
1427
1428
1429/**	Handler for SIGTERM signal.
1430	@param	signo	Signal number (always SIGTERM, ignored).
1431*/
1432static
1433void
1434sig_handler_term(int DK4_ARG_UNUSED(signo) )
1435{
1436  DK4_UNUSED_ARG(signo)
1437  *sig_pass_pointer(&sig_had_term) = 1;
1438}
1439
1440
1441/**	Read value from volatile atomic type.
1442	This function is necessary as some compilers mis-optimize
1443	direct access to volatile variables (at least if you believe
1444	one of the coding standards).
1445	@param	ap	Pointer to volatile atomic variable.
1446	@return	Contents of the variable.
1447*/
1448static
1449dk4_sig_atomic_t
1450sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap)
1451{
1452  return (*ap);
1453}
1454
1455
1456
1457/**	Check whether we can continue or must abort due to signal.
1458	@param	dopipe	Flag: Check for SIGPIPE too.
1459	@return	1 if we can continue, 0 otherwise.
1460*/
1461static
1462int
1463can_continue(int dohup, int dopipe)
1464{
1465  int		 back	= 1;
1466  $? "+ can_continue dohup=%d dopipe=%d", dohup, dopipe
1467  if (0 != sig_read_atomic(&sig_had_term)) { back = 0; }
1468  if (0 != sig_read_atomic(&sig_had_int))  { back = 0; }
1469#ifdef SIGHUP
1470  if (0 != dohup) {
1471    if (0 != sig_read_atomic(&sig_had_hup)) { back = 0; }
1472    if (1 > ccinner) { back = 0; }
1473  }
1474#endif
1475#ifdef SIGPIPE
1476  if (0 != dopipe) {
1477    if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; }
1478  }
1479#endif
1480  if (1 > ccouter) { back = 0; }
1481  $? "- can_continue %d", back
1482  return back;
1483}
1484
1485
1486
1487/**	Log a multi part message to file, either stderr or log file.
1488	@param	fipo		Output file.
1489	@param	timebuf		Text buffer containing timestamp.
1490	@param	sourcefile	Source file name.
1491	@param	linebuf		Text buffer containing source file line number.
1492	@param	error		Flag: Insert keyword "ERROR:"
1493	@param	shtime		Flag: Show timestamp from buffer.
1494	@param	shline		Flag: Show line number from buffer.
1495	@param	msgs		Array containing the message parts.
1496	@param	szmsgs		Number of elements in msgs array.
1497
1498*/
1499static
1500void
1501log_file_multipart_message(
1502  FILE			*fipo,
1503  const char		*timebuf,
1504  const char		*sourcefile,
1505  const char		*linebuf,
1506  int			 error,
1507  int			 shtime,
1508  int			 shline,
1509  const char * const	*msgs,
1510  size_t		 szmsgs
1511)
1512{
1513  size_t	i;
1514  $? "+ log_file_multipart_message %u", (unsigned)szmsgs
1515  if (0 != shtime) {
1516    fputs(printqd_kw[6], fipo);
1517    fputs(timebuf, fipo);
1518    fputs(printqd_kw[5], fipo);
1519  }
1520  if (NULL != sourcefile) {
1521    fputs(sourcefile, fipo);
1522    if (0 != shline) {
1523      fputs(printqd_kw[8], fipo);
1524	fputs(linebuf, fipo);
1525    }
1526    fputs(printqd_kw[7], fipo);
1527  }
1528  if (0 != error) {
1529    fputs(printqd_kw[9], fipo);
1530  }
1531  for (i = 0; i < szmsgs; i++) {
1532    if (NULL != msgs[i]) {
1533      fputs(msgs[i], fipo);
1534    }
1535  }
1536  fputs(printqd_kw[5], fipo);
1537  fflush(fipo);
1538  $? "- log_file_multipart_message"
1539}
1540
1541
1542
1543/**	Write a log message consisting of multiple parts.
1544	@param	sourcefile	Configuration file name.
1545	@param	sourceline	Configuration file line number.
1546	@param	sll		Syslog level.
1547	@param	msgs		Array containing the message parts.
1548	@param	szmsgs		Number of elements in msgs array.
1549*/
1550static
1551void
1552log_multipart_message(
1553  const char		*sourcefile,
1554  unsigned long		 sourceline,
1555  int			 sll,
1556  int			 error,
1557  char const * const	*msgs,
1558  size_t		 szmsgs
1559)
1560{
1561  char		 timebuf[64];
1562  char		 linebuf[64];
1563  FILE		*fipo;
1564  const char	*lfn;
1565  dk4_time_t	 current;
1566  int		 stests	= DK4_FOPEN_SC_PRIVILEGED;
1567  int		 shtime	= 0;
1568  int		 shline	= 0;
1569  $? "+ log_multipart_message %u", (unsigned)szmsgs
1570  /*	Check whether to prepend a timestamp line.
1571  */
1572  dk4time_get(&current);
1573  if (current != prev_log_time) {
1574    prev_log_time = current;
1575    if (0 != dk4time_as_text_c8(timebuf, sizeof(timebuf), &current, NULL)) {
1576      shtime = 1;
1577    }
1578  }
1579
1580  /*	Check whether to prepend source file name and line number.
1581  */
1582  if ((NULL != sourcefile) && ((dk4_um_t)0UL != sourceline)) {
1583    shline = dk4ma_write_c8_decimal_unsigned(
1584      linebuf, sizeof(linebuf), (dk4_um_t)sourceline, 0, NULL
1585    );
1586  }
1587
1588  /*	Output to log file.
1589  */
1590  lfn = conf.lname;
1591  if (NULL == lfn) { lfn = def_log_file_name; }
1592  fipo = dk4fopen_c8(lfn, printqd_kw[10], stests, NULL);
1593  if (NULL != fipo) {
1594    prev_filelog_failed = 0;
1595    log_file_multipart_message(
1596      fipo, timebuf, sourcefile, linebuf, error, shtime, shline, msgs, szmsgs
1597    );
1598    fclose(fipo);
1599  } else {
1600#if DK4_HAVE_SYSLOG
1601    if (0 == prev_filelog_failed) {
1602      openlog(printqd_kw[0], LOG_PID, syslogf);
1603      syslog(LOG_ERR, "%s%s%s", printqd_kw[170], lfn, printqd_kw[171]);
1604      closelog();
1605    }
1606#endif
1607    prev_filelog_failed = 1;
1608  }
1609
1610  /*	Output to stderr when debugging.
1611  */
1612  if (0 != debug) {
1613    log_file_multipart_message(
1614      stderr, timebuf, sourcefile, linebuf, error, shtime, shline, msgs, szmsgs
1615    );
1616  }
1617
1618  /*	Output to syslog.
1619  */
1620  if (LOG_EMERG != sll) {
1621#if DK4_HAVE_SYSLOG
1622    openlog(printqd_kw[0], LOG_PID, syslogf);
1623    if (1 < szmsgs) {
1624      if (2 < szmsgs) {
1625        if (3 < szmsgs) {
1626	  if (4 < szmsgs) {
1627	    if (5 < szmsgs) {
1628	      if (6 < szmsgs) {
1629	        if (7 < szmsgs) {
1630		  if (8 < szmsgs) {
1631                    syslog(
1632                      sll, "%s%s%s%s%s%s%s%s%s",
1633	              ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1634	              ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1635	              ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])),
1636	              ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])),
1637		      ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])),
1638		      ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])),
1639		      ((NULL != msgs[6]) ? (msgs[6]) : (printqd_kw[11])),
1640		      ((NULL != msgs[7]) ? (msgs[7]) : (printqd_kw[11])),
1641		      ((NULL != msgs[8]) ? (msgs[8]) : (printqd_kw[11]))
1642                    );
1643		  } else {
1644                    syslog(
1645                      sll, "%s%s%s%s%s%s%s%s",
1646	              ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1647	              ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1648	              ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])),
1649	              ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])),
1650		      ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])),
1651		      ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])),
1652		      ((NULL != msgs[6]) ? (msgs[6]) : (printqd_kw[11])),
1653		      ((NULL != msgs[7]) ? (msgs[7]) : (printqd_kw[11]))
1654                    );
1655		  }
1656		} else {
1657                  syslog(
1658                    sll, "%s%s%s%s%s%s%s",
1659	            ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1660	            ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1661	            ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])),
1662	            ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])),
1663		    ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])),
1664		    ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])),
1665		    ((NULL != msgs[6]) ? (msgs[6]) : (printqd_kw[11]))
1666                  );
1667		}
1668	      } else {
1669                syslog(
1670                  sll, "%s%s%s%s%s%s",
1671	          ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1672	          ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1673	          ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])),
1674	          ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])),
1675		  ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])),
1676		  ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11]))
1677                );
1678	      }
1679	    } else {
1680              syslog(
1681                sll, "%s%s%s%s%s",
1682	        ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1683	        ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1684	        ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])),
1685	        ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])),
1686		((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11]))
1687              );
1688	    }
1689	  } else {
1690            syslog(
1691              sll, "%s%s%s%s",
1692	      ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1693	      ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1694	      ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])),
1695	      ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11]))
1696            );
1697	  }
1698	} else {
1699          syslog(
1700            sll, "%s%s%s",
1701	    ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1702	    ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])),
1703	    ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11]))
1704          );
1705	}
1706      } else {
1707        syslog(
1708          sll, "%s%s",
1709	  ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])),
1710	  ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11]))
1711        );
1712      }
1713    } else {
1714      syslog(
1715        sll, "%s",
1716	((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11]))
1717      );
1718    }
1719    closelog();
1720#endif
1721  }
1722  $? "- log_multipart_message"
1723}
1724
1725
1726
1727static
1728void
1729log_1(
1730  const char		*sourcefile,
1731  unsigned long		 sourceline,
1732  int			 sll,
1733  int			 error,
1734  size_t		 i1
1735)
1736{
1737  const char		*msgs[2];
1738  $? "+ log_1"
1739  msgs[0] = printqd_kw[i1];
1740  msgs[1] = NULL;
1741  log_multipart_message(sourcefile, sourceline, sll, error, msgs, 1);
1742  $? "- log_1"
1743}
1744
1745
1746
1747static
1748void
1749log_3(
1750  const char		*sourcefile,
1751  unsigned long		 sourceline,
1752  int			 sll,
1753  int			 error,
1754  size_t		 i1,
1755  size_t		 i2,
1756  const char		*s1
1757)
1758{
1759  const char		*msgs[4];
1760  $? "+ log_3"
1761  msgs[0] = printqd_kw[i1];
1762  msgs[1] = s1;
1763  msgs[2] = printqd_kw[i2];
1764  msgs[3] = NULL;
1765  log_multipart_message(sourcefile, sourceline, sll, error, msgs, 3);
1766  $? "- log_3"
1767}
1768
1769
1770
1771static
1772void
1773log_5(
1774  const char		*sourcefile,
1775  unsigned long		 sourceline,
1776  int			 sll,
1777  int			 error,
1778  size_t		 i1,
1779  size_t		 i2,
1780  size_t		 i3,
1781  const char		*s1,
1782  const char		*s2
1783)
1784{
1785  const char		*msgs[6];
1786  $? "+ log_5"
1787  msgs[0] = printqd_kw[i1];
1788  msgs[1] = s1;
1789  msgs[2] = printqd_kw[i2];
1790  msgs[3] = s2;
1791  msgs[4] = printqd_kw[i3];
1792  msgs[5] = NULL;
1793  log_multipart_message(sourcefile, sourceline, sll, error, msgs, 5);
1794  $? "- log_5"
1795}
1796
1797
1798
1799/**	Set database entry (create or modify).
1800	@param	db	Database to modify.
1801	@param	k	Key.
1802	@param	v	Value.
1803	@param	erp	Error report, may be NULL.
1804	@return	1 on success, 0 on error.
1805*/
1806static
1807int
1808dbi_set(
1809  dk4_dbi_t	*db,
1810  const char	*k,
1811  const char	*v,
1812  dk4_er_t	*erp
1813)
1814{
1815  dk4_er_t	 er;
1816  int		 back	= 0;
1817
1818  dk4error_init(&er);
1819  back = dk4dbi_c8_set(db, k, v, &er);
1820  dk4error_copy(erp, &er);
1821  db_modified = 1;
1822  if (0 != back) {
1823    prev_dbmod_failed = 0;
1824    if (0 != conf.ldb) {
1825      log_5(NULL, 0UL, LOG_EMERG, 0, 122, 123, 124, k, v);
1826    }
1827  } else {
1828#if DK4_HAVE_SYSLOG
1829    if (0 == prev_dbmod_failed) {
1830      openlog(printqd_kw[0], LOG_PID, syslogf);
1831      syslog(LOG_ERR, "%s", printqd_kw[172]);
1832      closelog();
1833    }
1834#endif
1835    prev_dbmod_failed = 1;
1836    switch (er.ec) {
1837      case DK4_E_INVALID_ARGUMENTS : {
1838	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 125, k, v);
1839      } break;
1840      case DK4_E_SYNTAX : {
1841	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 126, k, v);
1842      } break;
1843      case DK4_E_MATH_OVERFLOW : {
1844	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 127, k, v);
1845      } break;
1846      case DK4_E_NOT_SUPPORTED : {
1847	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 128, k, v);
1848      } break;
1849      case DK4_E_NOT_FOUND : {
1850	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 129, k, v);
1851      } break;
1852      case DK4_E_MEMORY_ALLOCATION_FAILED : {
1853	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 130, k, v);
1854      } break;
1855      case DK4_E_WRITE_FAILED : {
1856	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 131, k, v);
1857      } break;
1858      default : {
1859	log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 132, k, v);
1860      } break;
1861    }
1862  }
1863  return back;
1864}
1865
1866
1867
1868/**	Retrieve database entry.
1869	@param	db	Database to retrieve data from.
1870	@param	k	Key text.
1871	@param	vbuf	Buffer for text value.
1872	@param	vsz	Size of buffer.
1873	@param	erp	Error report, may be NULL.
1874	@return	1 on success, 0 on error.
1875*/
1876static
1877int
1878dbi_get(
1879  dk4_dbi_t	*db,
1880  const char	*k,
1881  char		*vbuf,
1882  size_t	 vsz,
1883  dk4_er_t	*erp
1884)
1885{
1886  dk4_er_t	 er;
1887  int		 back	= 0;
1888
1889  dk4error_init(&er);
1890  back = dk4dbi_c8_get(db, k, vbuf, vsz, &er);
1891  dk4error_copy(erp, &er);
1892  if (0 != back) {
1893    if (0 != conf.ldb) {
1894      log_5(NULL, 0UL, LOG_EMERG, 0, 133, 123, 124, k, vbuf);
1895    }
1896  } else {
1897    switch (er.ec) {
1898      case DK4_E_INVALID_ARGUMENTS : {
1899	log_3(NULL, 0UL, LOG_EMERG, 1, 133, 125, k);
1900      } break;
1901      case DK4_E_MATH_OVERFLOW : {
1902	log_3(NULL, 0UL, LOG_EMERG, 1, 133, 127, k);
1903      } break;
1904      case DK4_E_NOT_SUPPORTED : {
1905	log_3(NULL, 0UL, LOG_EMERG, 1, 133, 128, k);
1906      } break;
1907      case DK4_E_NOT_FOUND : {
1908        if (0 != conf.ldb) {
1909	  log_3(NULL, 0UL, LOG_EMERG, 1, 133, 129, k);
1910	}
1911      } break;
1912      case DK4_E_BUFFER_TOO_SMALL : {
1913	log_3(NULL, 0UL, LOG_EMERG, 1, 133, 134, k);
1914      } break;
1915      default : {
1916	log_3(NULL, 0UL, LOG_EMERG, 1, 133, 132, k);
1917      } break;
1918    }
1919  }
1920  return back;
1921}
1922
1923
1924
1925/**	Delete DB entry if it exists.
1926	@param	db	Database to modify.
1927	@param	k	Key.
1928	@param	erp	Error report, may be NULL.
1929	@return	1 on success, 0 on error.
1930*/
1931static
1932int
1933dbi_del(
1934  dk4_dbi_t	*db,
1935  const char	*k,
1936  dk4_er_t	*erp
1937)
1938{
1939  dk4_er_t	 er;
1940  int		 back	= 0;
1941
1942  dk4error_init(&er);
1943  back = dk4dbi_c8_del(db, k, &er);
1944  dk4error_copy(erp, &er);
1945  db_modified = 1;
1946  if (0 != back) {
1947    prev_dbmod_failed = 0;
1948    if (0 != conf.ldb) {
1949      log_3(NULL, 0UL, LOG_EMERG, 0, 135, 124, k);
1950    }
1951  } else {
1952    if (DK4_E_NOT_FOUND != er.ec) {
1953#if DK4_HAVE_SYSLOG
1954      if (0 == prev_dbmod_failed) {
1955        openlog(printqd_kw[0], LOG_PID, syslogf);
1956	syslog(LOG_ERR, "%s", printqd_kw[172]);
1957	closelog();
1958      }
1959#endif
1960      prev_dbmod_failed = 1;
1961    }
1962    switch (er.ec) {
1963      case DK4_E_INVALID_ARGUMENTS : {
1964	log_3(NULL, 0UL, LOG_EMERG, 1, 135, 125, k);
1965      } break;
1966      case DK4_E_SYNTAX : {
1967	log_3(NULL, 0UL, LOG_EMERG, 1, 135, 126, k);
1968      } break;
1969      case DK4_E_MATH_OVERFLOW : {
1970	log_3(NULL, 0UL, LOG_EMERG, 1, 135, 127, k);
1971      } break;
1972      case DK4_E_NOT_SUPPORTED : {
1973	log_3(NULL, 0UL, LOG_EMERG, 1, 135, 128, k);
1974      } break;
1975      case DK4_E_WRITE_FAILED : {
1976	log_3(NULL, 0UL, LOG_EMERG, 1, 135, 131, k);
1977      } break;
1978      case DK4_E_NOT_FOUND : {
1979        if (0 != conf.ldb) {
1980	  log_3(NULL, 0UL, LOG_EMERG, 1, 135, 129, k);
1981	}
1982      } break;
1983      default : {
1984	log_3(NULL, 0UL, LOG_EMERG, 1, 135, 132, k);
1985      } break;
1986    }
1987  }
1988  return back;
1989}
1990
1991
1992
1993/**	Synchronize database modifications to disk, if any.
1994	@param	db	Database.
1995	@param	erp	Error report, may be NULL.
1996	@return	1 on success, 0 on error.
1997*/
1998static
1999int
2000dbi_sync(dk4_dbi_t *db, dk4_er_t *erp)
2001{
2002  dk4_er_t	 er;
2003  int		 back	= 0;
2004  if (0 != db_modified) {
2005    dk4error_init(&er);
2006    back = dk4dbi_sync(db, &er);
2007    dk4error_copy(erp, &er);
2008    if (0 != back) {
2009      db_modified = 0;
2010      prev_dbsync_failed = 0;
2011      if (0 != conf.ldb) {
2012        log_1(NULL, 0UL, LOG_EMERG, 0, 136);
2013      }
2014    } else {
2015#if DK4_HAVE_SYSLOG
2016      if (0 == prev_dbsync_failed) {
2017        openlog(printqd_kw[0], LOG_PID, syslogf);
2018	syslog(LOG_ERR, "%s", printqd_kw[173]);
2019	closelog();
2020      }
2021#endif
2022      prev_dbsync_failed = 1;
2023      switch (er.ec) {
2024	case DK4_E_INVALID_ARGUMENTS : {
2025	  log_1(NULL, 0UL, LOG_EMERG, 1, 137);
2026	} break;
2027	case DK4_E_NOT_SUPPORTED : {
2028	  log_1(NULL, 0UL, LOG_EMERG, 1, 138);
2029	} break;
2030	case DK4_E_SYNTAX : {
2031	  log_1(NULL, 0UL, LOG_EMERG, 1, 139);
2032	} break;
2033	case DK4_E_SEC_CHECK : {
2034	  log_1(NULL, 0UL, LOG_EMERG, 1, 140);
2035	} break;
2036	case DK4_E_OPEN_WRITE_FAILED : {
2037	  log_1(NULL, 0UL, LOG_EMERG, 1, 141);
2038	} break;
2039	case DK4_E_WRITE_FAILED : {
2040	  log_1(NULL, 0UL, LOG_EMERG, 1, 142);
2041	} break;
2042	case DK4_E_CLOSE_FAILED : {
2043	  log_1(NULL, 0UL, LOG_EMERG, 1, 143);
2044	} break;
2045	default : {
2046	  log_1(NULL, 0UL, LOG_EMERG, 1, 144);
2047	} break;
2048      }
2049    }
2050  } else {
2051    back = 1;
2052  }
2053  return back;
2054}
2055
2056
2057
2058/**	Compare two pqd_l_conn_t structures.
2059	@param	l	Left object.
2060	@param	r	Right object.
2061	@param	cr	Comparison criteria, ignored.
2062	@return	Comparison result.
2063*/
2064static
2065int
2066compare_pqd_l_conn_t(const void *l, const void *r, int DK4_ARG_UNUSED(cr) )
2067{
2068  const pqd_l_conn_t	*pl;
2069  const pqd_l_conn_t	*pr;
2070  int			 back	= 0;
2071  $? "+ compare_pqd_l_conn_t"
2072  DK4_UNUSED_ARG(cr)
2073  if (NULL != l) {
2074    if (NULL != r) {
2075      pl = (const pqd_l_conn_t *)l;
2076      pr = (const pqd_l_conn_t *)r;
2077      if (pl->sock > pr->sock) {
2078        back = 1;
2079      } else {
2080        if (pl->sock < pr->sock) {
2081	  back = -1;
2082	}
2083      }
2084    } else {
2085      back = 1;
2086    }
2087  } else {
2088    if (NULL != r)  { back = -1; }
2089  }
2090  $? "- compare_pqd_l_conn_t %d", back
2091  return back;
2092}
2093
2094
2095
2096/**	Compare two pqd_n_conn_t structures.
2097	@param	l	Left object.
2098	@param	r	Right object.
2099	@param	cr	Comparison criteria, ignored.
2100	@return	Comparison result.
2101*/
2102static
2103int
2104compare_pqd_n_conn_t(const void *l, const void *r, int DK4_ARG_UNUSED(cr) )
2105{
2106  const pqd_n_conn_t	*pl;
2107  const pqd_n_conn_t	*pr;
2108  int			 back	= 0;
2109  $? "+ compare_pqd_n_conn_t"
2110  DK4_UNUSED_ARG(cr)
2111  if (NULL != l) {
2112    if (NULL != r) {
2113      pl = (const pqd_n_conn_t *)l;
2114      pr = (const pqd_n_conn_t *)r;
2115      if (pl->sock > pr->sock) {
2116        back = 1;
2117      } else {
2118        if (pl->sock < pr->sock) {
2119	  back = -1;
2120	}
2121      }
2122    } else {
2123      back = 1;
2124    }
2125  } else {
2126    if (NULL != r)  { back = -1; }
2127  }
2128  $? "- compare_pqd_n_conn_t %d", back
2129  return back;
2130}
2131
2132
2133
2134/**	Delete a limit structure (release memory).
2135	@param	ptr	Limit to delete.
2136*/
2137static
2138void
2139limit_delete(pqd_limit_t *ptr)
2140{
2141  $? "+ limit_delete %s", TR_8PTR(ptr)
2142  if (NULL != ptr) {		$? ". name = \"%s\"", TR_8STR(ptr->name)
2143    dk4mem_release(ptr->name);
2144    ptr->limit = (dk4_um_t)0UL;
2145    dk4mem_free(ptr);
2146  }
2147  $? "- limit_delete"
2148}
2149
2150
2151
2152/**	Create limit structure (allocate memory).
2153	@param	name	User or group name.
2154	@param	max	Maximum page number.
2155	@return	Valid pointer on success, NULL on error.
2156*/
2157static
2158pqd_limit_t *
2159limit_new(const char *name, dk4_um_t max)
2160{
2161  pqd_limit_t	*back	= NULL;
2162  $? "+ limit_new \"%s\" %lu", TR_8STR(name), (unsigned long)max
2163  if (NULL != name) {				$? ". name ok"
2164    back = dk4mem_new(pqd_limit_t,1,NULL);
2165    if (NULL != back) {				$? ". allocation ok"
2166      back->limit = max;
2167      back->name  = dk4str8_dup(name, NULL);
2168      if (NULL == back->name) {			$? "! allocation (2)"
2169        limit_delete(back);
2170	back = NULL;
2171      }
2172    }
2173#if TRACE_DEBUG
2174    else {					$? "! allocation (1)"
2175    }
2176#endif
2177  } $? "- limit_new %s", TR_8PTR(back)
2178  return back;
2179}
2180
2181
2182
2183/**	Compare limit structures.
2184	@param	l	Left object.
2185	@param	r	Right object.
2186	@param	cr	Comparison criteria (0=limit/limit, 1=limit/name).
2187	@return	Comparison result.
2188*/
2189static
2190int
2191limit_compare(const void *l, const void *r, int cr)
2192{
2193  const	pqd_limit_t	*pl;
2194  const	pqd_limit_t	*pr;
2195  $? "+ limit_compare"
2196  int		 back	= 0;
2197  if (NULL != l) {
2198    if (NULL != r) {
2199      pl = (const pqd_limit_t *)l;
2200      switch (cr) {
2201        case 1: {
2202	  if (NULL != pl->name) {
2203	    back = strcmp(pl->name, (const char *)r);
2204	    if (-1 > back) { back = -1; }
2205	    if ( 1 < back) { back =  1; }
2206	  } else { back = -1; }
2207	} break;
2208	default: {
2209	  pr = (const pqd_limit_t *)r;
2210	  if (NULL != pl->name) {
2211	    if (NULL != pr->name) {
2212	      back = strcmp(pl->name, pr->name);
2213	      if (-1 > back) { back = -1; }
2214	      if ( 1 < back) { back =  1; }
2215	    } else { back = 1; }
2216	  } else {
2217	    if (NULL != pr->name) { back = -1; }
2218	  }
2219	} break;
2220      }
2221    } else { back = 1; }
2222  } else {
2223    if (NULL != r) { back = -1; }
2224  } $? "- limit_compare %d", back
2225  return  back;
2226}
2227
2228
2229
2230/**	Delete all limits in a storage.
2231	@param	it	Storage iterator.
2232*/
2233static
2234void
2235limit_all_delete(dk4_sto_it_t *it)
2236{
2237  pqd_limit_t	*ptr;
2238
2239  $? "+ limit_all_delete %s", TR_8PTR(it)
2240  dk4sto_it_reset(it);
2241  do {
2242    ptr = (pqd_limit_t *)dk4sto_it_next(it);
2243    if (NULL != ptr) {
2244      limit_delete(ptr);
2245    }
2246  } while (NULL != ptr);
2247  dk4sto_it_close(it);
2248  $? "- limit_all_delete"
2249}
2250
2251
2252
2253/**	Delete a class structure (release memory).
2254	@param	ptr	Class to delete.
2255*/
2256static
2257void
2258class_delete(pqd_class_t *ptr)
2259{
2260  $? "+ class_delete %s", TR_8PTR(ptr)
2261  if (NULL != ptr) {			$? ". ptr ok"
2262    dk4mem_release(ptr->name);
2263    if (NULL != ptr->i_u) {
2264      limit_all_delete(ptr->i_u);
2265    } ptr->i_u = NULL;
2266    if (NULL != ptr->s_u) {
2267      dk4sto_close(ptr->s_u);
2268    } ptr->s_u = NULL;
2269    if (NULL != ptr->i_g) {
2270      limit_all_delete(ptr->i_g);
2271    } ptr->i_g = NULL;
2272    if (NULL != ptr->s_g) {
2273      dk4sto_close(ptr->s_g);
2274    } ptr->s_g = NULL;
2275    ptr->dl = (dk4_um_t)0UL;
2276    ptr->da = 0;
2277    dk4mem_free(ptr);
2278  }
2279#if TRACE_DEBUG
2280  else {				$? "! ptr"
2281  }
2282#endif
2283  $? "- class_delete"
2284}
2285
2286
2287
2288/**	Delete all classes from a containter.
2289	@param	it	Container iterator.
2290*/
2291static
2292void
2293class_all_delete(dk4_sto_it_t *it)
2294{
2295  pqd_class_t	*ptr;
2296  $? "+ class_all_delete %s", TR_8PTR(it)
2297  dk4sto_it_reset(it);
2298  do {
2299    ptr = (pqd_class_t *)dk4sto_it_next(it);
2300    if (NULL != ptr) {
2301      class_delete(ptr);
2302    }
2303  } while (NULL != ptr);
2304  dk4sto_it_close(it);
2305  $? "- class_all_delete"
2306}
2307
2308
2309
2310/**	Create a class structure (allocate memory).
2311	@param	n	Class name.
2312	@return	Valid pointer on success, NULL on error.
2313*/
2314static
2315pqd_class_t *
2316class_new(const char *n)
2317{
2318  pqd_class_t		*back	= NULL;
2319  int			 ok	= 0;
2320  $? "+ class_new \"%s\"", TR_8STR(n)
2321  if (NULL != n) {				$? ". n ok"
2322    back = dk4mem_new(pqd_class_t,1,NULL);
2323    if (NULL != back) {
2324      back->s_u = NULL;
2325      back->i_u = NULL;
2326      back->s_g = NULL;
2327      back->i_g = NULL;
2328      back->dl  = (dk4_um_t)0UL;
2329      back->da  = 0;
2330      back->hdl = 0;
2331      back->name = dk4str8_dup(n, NULL);
2332      if (NULL != back->name) {
2333        back->s_u = dk4sto_open(NULL);
2334	if (NULL != back->s_u) {
2335	  dk4sto_set_comp(back->s_u, limit_compare, 0);
2336	  back->i_u = dk4sto_it_open(back->s_u, NULL);
2337	  if (NULL != back->i_u) {
2338	    back->s_g = dk4sto_open(NULL);
2339	    if (NULL != back->s_g) {
2340	      dk4sto_set_comp(back->s_g, limit_compare, 0);
2341	      back->i_g = dk4sto_it_open(back->s_g, NULL);
2342	      if (NULL != back->i_g) {
2343	        ok = 1;				$? ". allocations ok"
2344	      }
2345#if TRACE_DEBUG
2346	      else {				$? "! allocation (6)"
2347	      }
2348#endif
2349	    }
2350#if TRACE_DEBUG
2351	    else {				$? "! allocation (5)"
2352	    }
2353#endif
2354	  }
2355#if TRACE_DEBUG
2356	  else {				$? "! allocation (4)"
2357	  }
2358#endif
2359	}
2360#if TRACE_DEBUG
2361	else {					$? "! allocation (3)"
2362	}
2363#endif
2364      }
2365#if TRACE_DEBUG
2366      else {					$? "! allocation (2)"
2367      }
2368#endif
2369      if (0 == ok) {				$? "! allocations"
2370        class_delete(back);
2371	back = NULL;
2372      }
2373    }
2374#if TRACE_DEBUG
2375    else {					$? "! allocation (1)"
2376    }
2377#endif
2378  }
2379#if TRACE_DEBUG
2380  else {					$? "! n"
2381  }
2382#endif
2383  $? "- class_new %s", TR_8PTR(back)
2384  return back;
2385}
2386
2387
2388
2389/**	Compare classes.
2390	@param	l	Left object.
2391	@param	r	Right object.
2392	@param	cr	Comparison criteria (0=class/class, 1=class/name).
2393	@return	Comparison result.
2394*/
2395static
2396int
2397class_compare(const void *l, const void *r, int cr)
2398{
2399  const	pqd_class_t	*pl;
2400  const	pqd_class_t	*pr;
2401  int		 back	= 0;
2402  $? "+ class_compare"
2403  if (NULL != l) {
2404    if (NULL != r) {
2405      pl = (const pqd_class_t *)l;
2406      switch (cr) {
2407        case 1: {
2408	  if (NULL != pl->name) {
2409	    back = strcmp(pl->name, (const char *)r);
2410	    if (-1 > back) { back = -1; }
2411	    if ( 1 < back) { back =  1; }
2412	  } else { back = -1; }
2413	} break;
2414	default: {
2415	  pr = (const pqd_class_t *)r;
2416	  if (NULL != pl->name) {
2417	    if (NULL != pr->name) {
2418	      back = strcmp(pl->name, pr->name);
2419	      if (-1 > back) { back = -1; }
2420	      if ( 1 < back) { back =  1; }
2421	    } else { back = 1; }
2422	  } else {
2423	    if (NULL != pr->name) { back = -1; }
2424	  }
2425	} break;
2426      }
2427    } else { back = 1; }
2428  } else {
2429    if (NULL != r) { back = -1; }
2430  } $? "- class_compare %d", back
2431  return back;
2432}
2433
2434
2435
2436/**	Delete a printer structure (release memory).
2437	@param	ptr	Printer structure to delete.
2438*/
2439static
2440void
2441printer_delete(pqd_printer_t *ptr)
2442{
2443  $? "+ printer_delete %s", TR_8PTR(ptr)
2444  if (NULL != ptr) {			$? ". name = \"%s\"", TR_8STR(ptr->name)
2445    dk4mem_release(ptr->name);
2446    ptr->cl = NULL;
2447    dk4mem_free(ptr);
2448  }
2449  $? "- printer_delete"
2450}
2451
2452
2453
2454/**	Create printer structure (allocate memory).
2455	@param	n	Printer name.
2456	@return	Valid pointer on success, NULL on error.
2457*/
2458static
2459pqd_printer_t *
2460printer_new(const char *n)
2461{
2462  pqd_printer_t		*back	= NULL;
2463  $? "+ printer_new \"%s\"", TR_8STR(n)
2464  if (NULL != n) {
2465    back = dk4mem_new(pqd_printer_t,1,NULL);
2466    if (NULL != back) {
2467      back->cl = NULL;
2468      back->name = dk4str8_dup(n,NULL);
2469      if (NULL == back->name) {			$? "! allocation (2)"
2470        printer_delete(back);
2471	back = NULL;
2472      }
2473    }
2474#if TRACE_DEBUG
2475    else {					$? "! allocation (1)"
2476    }
2477#endif
2478  }
2479#if TRACE_DEBUG
2480  else {					$? "! n"
2481  }
2482#endif
2483  $? "- printer_new %s", TR_8PTR(back)
2484  return back;
2485}
2486
2487
2488
2489/**	Compare two printers.
2490	@param	l	Left object.
2491	@param	r	Right object.
2492	@param	cr	Comparison criteria (0=printer/printer, 1=printer/name).
2493	@return	Comparison result.
2494*/
2495static
2496int
2497printer_compare(const void *l, const void *r, int cr)
2498{
2499  const pqd_printer_t	*pl;
2500  const	pqd_printer_t	*pr;
2501  int			 back	= 0;
2502  $? "+ printer_compare"
2503  if (NULL != l) {
2504    if (NULL != r) {
2505      pl = (const pqd_printer_t *)l;
2506      switch (cr) {
2507        case 1: {
2508	  if (NULL != pl->name) {
2509	    back = strcmp(pl->name, (const char *)r);
2510	    if (-1 > back) { back = -1; }
2511	    if ( 1 < back) { back =  1; }
2512	  } else { back = -1; }
2513	} break;
2514	default: {
2515	  pr = (const pqd_printer_t *)r;
2516	  if (NULL != pl->name) {
2517	    if (NULL != pr->name) {
2518	      back = strcmp(pl->name, pr->name);
2519	      if (-1 > back) { back = -1; }
2520	      if ( 1 < back) { back =  1; }
2521	    } else { back = 1; }
2522	  } else {
2523	    if (NULL != pr->name) { back = -1; }
2524	  }
2525	} break;
2526      }
2527    } else { back = 1; }
2528  } else {
2529    if (NULL != r) { back = -1; }
2530  } $? "- printer_compare %d", back
2531  return back;
2532}
2533
2534
2535
2536/**	Delete all printers from a container.
2537	@param	it	Container iterator.
2538*/
2539static
2540void
2541printer_all_delete(dk4_sto_it_t *it)
2542{
2543  pqd_printer_t	*ptr;
2544  $? "+ printer_all_delete %s", TR_8PTR(it)
2545  dk4sto_it_reset(it);
2546  do {
2547    ptr = (pqd_printer_t *)dk4sto_it_next(it);
2548    if (NULL != ptr) {
2549      printer_delete(ptr);
2550    }
2551  } while (NULL != ptr);
2552  dk4sto_it_close(it);
2553  $? "- printer_all_delete"
2554}
2555
2556
2557
2558/**	Delete printer alias structure (release memory).
2559	@param	ptr	Structure to delete.
2560*/
2561static
2562void
2563printer_alias_delete(pqd_printer_alias_t *ptr)
2564{
2565  $? "+ printer_alias_delete %s", TR_8PTR(ptr)
2566  if (NULL != ptr) {		$? ". name = \"%s\"", TR_8STR(ptr->name)
2567    dk4mem_release(ptr->name);
2568    ptr->pr = NULL;
2569    dk4mem_free(ptr);
2570  }
2571  $? "- printer_alias_delete"
2572}
2573
2574
2575
2576/**	Create printer alias structure (allocate memory).
2577	@param	n	Alias name.
2578	@param	pr	Printer for alias.
2579	@return	Valid pointer on success, NULL on error.
2580*/
2581static
2582pqd_printer_alias_t *
2583printer_alias_new(const char *n, pqd_printer_t *pr)
2584{
2585  pqd_printer_alias_t	*back	= NULL;
2586  $? "+ printer_alias_new \"%s\" %s", TR_8STR(n), TR_8PTR(pr)
2587  if ((NULL != n) && (NULL != pr)) {
2588    back = dk4mem_new(pqd_printer_alias_t,1,NULL);
2589    if (NULL != back) {
2590      back->pr = pr;
2591      back->name = dk4str8_dup(n,NULL);
2592      if (NULL == back->name) {
2593        printer_alias_delete(back);
2594	back = NULL;
2595      }
2596#if TRACE_DEBUG
2597      else {				$? "! allocation (2)"
2598      }
2599#endif
2600    }
2601#if TRACE_DEBUG
2602    else {				$? "! allocation (1)"
2603    }
2604#endif
2605  }
2606#if TRACE_DEBUG
2607  else {				$? "! n or pr"
2608  }
2609#endif
2610  $? "- printer_alias_new %s", TR_8PTR(back)
2611  return back;
2612}
2613
2614
2615
2616/**	Compare printer alias structures.
2617	@param	l	Left object.
2618	@param	r	Right object.
2619	@param	cr	Comparison criteria (0=alias/alias, 1=alias/name).
2620	@return	Comparison result.
2621*/
2622static
2623int
2624printer_alias_compare(const void *l, const void *r, int cr)
2625{
2626  const	pqd_printer_alias_t	*pl;
2627  const	pqd_printer_alias_t	*pr;
2628  int				 back	= 0;
2629  $? "+ printer_alias_compare"
2630  if (NULL != l) {
2631    if (NULL != r) {
2632      pl = (const pqd_printer_alias_t *)l;
2633      switch (cr) {
2634        case 1: {
2635	  if (NULL != pl->name) {
2636	    back = strcmp(pl->name, (const char *)r);
2637	    if (-1 > back) { back = -1; }
2638	    if ( 1 < back) { back =  1; }
2639	  } else { back = -1; }
2640	} break;
2641	default: {
2642	  pr = (const pqd_printer_alias_t *)r;
2643	  if (NULL != pl->name) {
2644	    if (NULL != pr->name) {
2645	      back = strcmp(pl->name, pr->name);
2646	      if (-1 > back) { back = -1; }
2647	      if ( 1 < back) { back =  1; }
2648	    } else { back = 1; }
2649	  } else {
2650	    if (NULL != pr->name) { back = -1; }
2651	  }
2652	} break;
2653      }
2654    } else { back = 1; }
2655  } else {
2656    if (NULL != r) { back = -1; }
2657  } $? "- printer_alias_compare %d", back
2658  return back;
2659}
2660
2661
2662
2663/**	Delete all printer alias structures in a container.
2664	@param	it	Container iterator.
2665*/
2666static
2667void
2668printer_alias_all_delete(dk4_sto_it_t *it)
2669{
2670  pqd_printer_alias_t	*ptr;
2671  $? "+ printer_alias_all_delete %s", TR_8PTR(it)
2672  dk4sto_it_reset(it);
2673  do {
2674    ptr = (pqd_printer_alias_t *)dk4sto_it_next(it);
2675    if (NULL != ptr) {
2676      printer_alias_delete(ptr);
2677    }
2678  } while (NULL != ptr);
2679  dk4sto_it_close(it);
2680  $? "- printer_alias_all_delete"
2681}
2682
2683
2684
2685/**	Initialize configuration before reading configuration file.
2686*/
2687static
2688void
2689config_init(void)
2690{
2691  $? "+ config_init"
2692  DK4_MEMRES(&conf, sizeof(conf));
2693  conf.sname = NULL;
2694  conf.dname = NULL;
2695  conf.lname = NULL;
2696  conf.s_c = NULL;
2697  conf.i_c = NULL;
2698  conf.s_p = NULL;
2699  conf.i_p = NULL;
2700  conf.s_a = NULL;
2701  conf.i_a = NULL;
2702  conf.s_ai = NULL;
2703  conf.i_ai = NULL;
2704  conf.s_ad = NULL;
2705  conf.i_ad = NULL;
2706  conf.s_aa = NULL;
2707  conf.i_aa = NULL;
2708  conf.m_loc = 0;	/* Maximum 0 means unlimited number of connections. */
2709  conf.m_tcp = 0;	/* Maximum 0 means unlimited number of connections. */
2710  conf.uid  = 0;
2711  conf.gid  = 0;
2712  conf.suid = 0;
2713  conf.sgid = 0;
2714  conf.slbl = 5;
2715  conf.snbl = 5;
2716  conf.iglo = 0;
2717  conf.dglo = 0;
2718  conf.aglo = 0;
2719  conf.ptcp = 0;
2720  conf.pudp = 0;
2721  conf.linf = 0;
2722  conf.ldb  = 0;
2723  $? "- config_init"
2724}
2725
2726
2727
2728/**	Allocate internal structures for configuration.
2729	@return	1 on success, 0 on error.
2730*/
2731static
2732int
2733config_allocate(void)
2734{
2735  int		 back	= 0;
2736  $? "+ config_allocate"
2737  conf.s_c  = dk4sto_open(NULL);
2738  conf.s_p  = dk4sto_open(NULL);
2739  conf.s_a  = dk4sto_open(NULL);
2740  conf.s_ai = dk4sto_open(NULL);
2741  conf.s_ad = dk4sto_open(NULL);
2742  conf.s_aa = dk4sto_open(NULL);
2743  if ((NULL != conf.s_ai) && (NULL != conf.s_ad) && (NULL != conf.s_aa)) {
2744  if ((NULL != conf.s_c) && (NULL != conf.s_p) && (NULL != conf.s_a)) {
2745    dk4sto_set_comp(conf.s_c, class_compare, 0);
2746    dk4sto_set_comp(conf.s_p, printer_compare, 0);
2747    dk4sto_set_comp(conf.s_a, printer_alias_compare, 0);
2748    dk4sto_set_comp(conf.s_ai, dk4socket_allowed_peer_compare, 0);
2749    dk4sto_set_comp(conf.s_ad, dk4socket_allowed_peer_compare, 0);
2750    dk4sto_set_comp(conf.s_aa, dk4socket_allowed_peer_compare, 0);
2751    conf.i_c  = dk4sto_it_open(conf.s_c, NULL);
2752    conf.i_p  = dk4sto_it_open(conf.s_p, NULL);
2753    conf.i_a  = dk4sto_it_open(conf.s_a, NULL);
2754    conf.i_ai = dk4sto_it_open(conf.s_ai, NULL);
2755    conf.i_ad = dk4sto_it_open(conf.s_ad, NULL);
2756    conf.i_aa = dk4sto_it_open(conf.s_aa, NULL);
2757    if ((NULL != conf.i_ai) && (NULL != conf.i_ad) && (NULL != conf.i_aa)) {
2758    if ((NULL != conf.i_c) && (NULL != conf.i_p) && (NULL != conf.i_a)) {
2759      back = 1;
2760    }
2761#if TRACE_DEBUG
2762    else {					$? "! allocation (4)"
2763    }
2764#endif
2765    }
2766#if TRACE_DEBUG
2767    else {					$? "! allocation (3)"
2768    }
2769#endif
2770  }
2771#if TRACE_DEBUG
2772  else {					$? "! allocation (2)"
2773  }
2774#endif
2775  }
2776#if TRACE_DEBUG
2777  else {					$? "! allocation (1)"
2778  }
2779#endif
2780  $? "- config_allocate %d", back
2781  return back;
2782}
2783
2784
2785
2786/**	Set backlog variable.
2787	@param	iptr	Address of backlog number variable.
2788	@param	pv	Text containing the number.
2789	@param	fn	File name for diagnostics.
2790	@param	lineno	Line number.
2791	@return	1 on success, 0 on error.
2792*/
2793static
2794int
2795set_backlog(int *iptr, char *pv, const char *fn, unsigned long lineno)
2796{
2797  const char	*ep	= NULL;
2798  int		 i	= 0;
2799  int		 back	= 0;
2800  int		 res	= 0;
2801  $? "+ set_backlog %s \"%s\"", TR_8PTR(iptr), TR_8STR(pv)
2802  res = dk4ma_input_c8_dec_int(&i, pv, &ep, 1, NULL);
2803  if (0 != res) {
2804    if (0 < i) {
2805      *iptr = i;
2806      back = 1;
2807    } else {					$? "! not positive"
2808      /* ERROR: Must be positive */
2809      log_1(fn, lineno, LOG_EMERG, 1, 15);
2810    }
2811  } else {					$? "! not integer"
2812    /* ERROR: Not an integer number */
2813    log_5(fn, lineno, LOG_EMERG, 1, 12, 13, 14, pv, ep);
2814  } $? "- set_backlog %d", back
2815  return back;
2816}
2817
2818
2819
2820/**	Set maximum number of connections.
2821	@param	pmaxconn	Address of result variable.
2822	@param	pv		Text containing the number.
2823	@param	fn		File name for diagnostics.
2824	@param	lineno		Line number.
2825	@return	1 on success, 0 on error.
2826*/
2827static
2828int
2829set_max_connections(
2830  size_t	*pmaxconn,
2831  char		*pv,
2832  const char	*fn,
2833  unsigned long	 lineno
2834)
2835{
2836  const char	*ep	= NULL;
2837  size_t	 sz	= 0;
2838  int		 back	= 0;
2839  int		 res	= 0;
2840  $? "+ set_max_connections %s \"%s\"", TR_8PTR(pmaxconn), TR_8STR(pv)
2841  res = dk4ma_input_c8_dec_size_t(&sz, pv, &ep, 1, NULL);
2842  if (0 != res) {
2843    *pmaxconn = sz;
2844    back = 1;
2845  } else {					$? "! not a size"
2846    /* ERROR: Not a size specification */
2847    log_5(fn, lineno, LOG_EMERG, 1, 16, 17, 18, pv, ep);
2848  }
2849  $? "- set_max_connections %d", back
2850  return back;
2851}
2852
2853
2854
2855/**	Set port number from text.
2856	@param	ppn	Address of port number variable.
2857	@param	pv	Text containing the number.
2858	@return	1 on success, 0 on error.
2859*/
2860static
2861int
2862set_port_number(
2863  unsigned short	*ppn,
2864  char			*pv,
2865  const char		*fn,
2866  unsigned long		 lineno
2867)
2868{
2869  const char	*ep	= NULL;
2870  unsigned short pn	= 0;
2871  int		 back	= 0;
2872  int		 res	= 0;
2873  $? "+ set_port_number %s \"%s\"", TR_8PTR(ppn), TR_8STR(pv)
2874  res = dk4ma_input_c8_dec_ushort(&pn, pv, &ep, 1, NULL);
2875  if (0 != res) {
2876    if (0 < pn) {
2877      *ppn = pn;
2878      back = 1;
2879    } else {					$? "! 0 not allowed"
2880      /* ERROR: Port number must not be 0 */
2881      log_1(fn, lineno, LOG_EMERG, 1, 22);
2882    }
2883  } else {					$? "! not unsigned short"
2884    /* ERROR: Not a 16 bit unsigned number */
2885    log_5(fn, lineno, LOG_EMERG, 1, 19, 20, 21, pv, ep);
2886  }
2887  $? "- set_port_number %d", back
2888  return back;
2889}
2890
2891
2892
2893/**	Add address to list of allowed hosts.
2894	@param	s	Containter of allowed peers.
2895	@param	iptr	Address of global flag variable.
2896	@param	str	Text containing the address.
2897	@return	1 on success, 0 on error.
2898*/
2899static
2900int
2901allow_hosts(
2902  dk4_sto_t	*s,
2903  int		*iptr,
2904  char		*str,
2905  const char	*filename,
2906  unsigned long	 lineno
2907)
2908{
2909  dk4_allowed_peer_t	 ap;
2910  dk4_allowed_peer_t	*pap;
2911  char			*px	= NULL;
2912  int			 res	= 0;
2913  int			 back	= 0;
2914  $? "+ allow_hosts %s %s \"%s\"", TR_8PTR(s), TR_8PTR(iptr), TR_8STR(str)
2915  px = dk4str8_next(str, NULL);
2916  if (NULL == px) {
2917    if (0 == strcmp(str, printqd_kw[2])) {
2918      *iptr = 1;
2919      back = 1;
2920    } else {
2921      DK4_MEMRES(&ap, sizeof(ap));
2922      res = dk4socket_c8_get_allowed_peer(&ap, str, NULL);
2923      if (DK4_SOCKET_RESULT_SUCCESS == res) {
2924        pap = dk4mem_new(dk4_allowed_peer_t,1,NULL);
2925	if (NULL != pap) {
2926	  DK4_MEMCPY(pap, &ap, sizeof(ap));
2927	  if (0 != dk4sto_add(s, pap, NULL)) {
2928	    back = 1;
2929	  } else {				$? "! storage add"
2930	    dk4mem_free(pap);
2931	    /* ERROR: Memory */
2932	    log_1(filename, lineno, LOG_EMERG, 1, 27);
2933	  }
2934	} else {				$? "! allocation"
2935	  /* ERROR: Memory */
2936	  log_1(filename, lineno, LOG_EMERG, 1, 27);
2937	}
2938      } else {					$? "! not a peer definition"
2939        /* ERROR: Syntax, not a peer definition */
2940	log_3(filename, lineno, LOG_EMERG, 1, 25, 26, str);
2941      }
2942    }
2943  } else {					$? "! unexpected text"
2944    /* ERROR: Unexpected text */
2945    log_3(filename, lineno, LOG_EMERG, 1, 23, 24, px);
2946  }
2947  $? "- allow_hosts %d", back
2948  return back;
2949}
2950
2951
2952
2953/**	Obtain limit value from text.
2954	@param	plim	Address of limit variable to set.
2955	@param	src	Text containing the limit.
2956	@return	1 on success, 0 on error.
2957*/
2958static
2959int
2960get_limit_value(dk4_um_t *plim, char *src, const char *fn, unsigned long lineno)
2961{
2962  const char	*ep	= NULL;
2963  dk4_um_t	 value	= (dk4_um_t)0UL;
2964  int		 back	= 0;
2965  int		 res	= 0;
2966  $? "+ get_limit_value %s \"%s\"", TR_8PTR(plim), TR_8STR(src)
2967  if (0 == strcmp(src, printqd_kw[3])) {
2968    *plim = DK4_UM_MAX;
2969    back  = 1;
2970  } else {
2971    if (0 == strcmp(src, printqd_kw[4])) {
2972      *plim = value;
2973      back  = 1;
2974    } else {
2975      res = dk4ma_input_c8_dec_dk4_um_t(&value, src, &ep, 1, NULL);
2976      if (0 != res) {
2977        *plim = value;
2978	back  = 1;
2979      } else {				$? "! not an unsigned number"
2980        /* ERROR: Syntax, not an unsigned number */
2981        log_5(fn, lineno, LOG_EMERG, 1, 28, 29, 30, src, ep);
2982      }
2983    }
2984  }
2985  $? "- get_limit_value %d", back
2986  return back;
2987}
2988
2989
2990
2991/**	Add a user or group limit to a class.
2992	@param	s	Container for limits.
2993	@param	i	Container iterator.
2994	@param	str	Text containing name and limit.
2995	@param	fn	File name.
2996	@param	lineno	Line number.
2997	@return	1 on success, 0 on error.
2998*/
2999static
3000int
3001add_limit(
3002  dk4_sto_t	*s,
3003  dk4_sto_it_t	*i,
3004  char		*str,
3005  const char	*fn,
3006  dk4_um_t	 lineno
3007)
3008{
3009  pqd_limit_t	*plim;
3010  char		*pnum;
3011  char		*px;
3012  dk4_um_t	 limval	= (dk4_um_t)0UL;
3013  int		 back	= 0;
3014  $? "+ add_limit %s %s \"%s\"", TR_8PTR(s), TR_8PTR(i), TR_8STR(str)
3015  pnum	= dk4str8_next(str, NULL);
3016  if (NULL != pnum) {
3017    px = dk4str8_next(pnum, NULL);
3018    if (NULL == px) {
3019      if (0 != get_limit_value(&limval, pnum, fn, lineno)) {
3020        plim = (pqd_limit_t *)dk4sto_it_find_like(i, str, 1);
3021	if (NULL == plim) {
3022	  plim = limit_new(str, limval);
3023	  if (NULL != plim) {
3024	    if (0 != dk4sto_add(s, plim, NULL)) {
3025	      back = 1;
3026	    } else {				$? "! allocation (2)"
3027	      /* ERROR: Memory */
3028	      log_1(fn, lineno, LOG_EMERG, 1, 27);
3029	      limit_delete(plim);
3030	    }
3031	  } else {				$? "! allocation (1)"
3032	    /* ERROR: Memory */
3033	    log_1(fn, lineno, LOG_EMERG, 1, 27);
3034	  }
3035	} else {				$? "! redefinition"
3036	  /* ERROR: Limit redefinition */
3037	  log_3(fn, lineno, LOG_EMERG, 1, 32, 33, str);
3038	}
3039      }
3040    } else {					$? "! unexpected text"
3041      /* ERROR: Syntax, no further text expected */
3042      log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3043    }
3044  } else {					$? "! no limit text"
3045    /* ERROR: Missing limit. */
3046    log_1(fn, lineno, LOG_EMERG, 1, 31);
3047  }
3048  $? "- add_limit %d", back
3049  return back;
3050}
3051
3052
3053
3054/**	Set default limit for class.
3055	@param	pcl	Class to modify.
3056	@param	pv	String containing the limit.
3057	@return	1 on success, 0 on error.
3058*/
3059static
3060int
3061set_default_limit(
3062  pqd_class_t	*pcl,
3063  char 		*pv,
3064  const char 	*fn,
3065  unsigned long	 lineno
3066)
3067{
3068  char		*px;
3069  dk4_um_t	 value	= (dk4_um_t)0UL;
3070  int		 back	= 0;
3071  $? "+ set_default_limit %s \"%s\"", TR_8PTR(pcl), TR_8STR(pv)
3072  px = dk4str8_next(pv, NULL);
3073  if (NULL == px) {
3074    if (0 != get_limit_value(&value, pv, fn, lineno)) {
3075      if ((0 == (1 & (pcl->hdl))) || (value == pcl->dl) ) {
3076        pcl->hdl |= 1;
3077	pcl->dl  =  value;
3078	back	 =  1;
3079      } else {					$? "! redefinition"
3080        /* ERROR: Syntax, default limit redefined */
3081	log_1(fn, lineno, LOG_EMERG, 1, 34);
3082      }
3083    }
3084  } else {					$? "! unexpected text"
3085    /* ERROR: No further text expected */
3086    log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3087  }
3088  $? "- set_default_limit %d", back
3089  return back;
3090}
3091
3092
3093
3094/**	Set deny action for printer class.
3095	@param	pcl	Class to modify.
3096	@param	pv	String containing the action.
3097	@return	1 on success, 0 on error.
3098*/
3099static
3100int
3101set_deny_action(
3102  pqd_class_t	*pcl,
3103  char		*pv,
3104  const char	*fn,
3105  unsigned long	 lineno
3106)
3107{
3108  char		*px	= NULL;
3109  int		 back	= 0;
3110  int		 res	= 0;
3111  $? "+ set_deny_action %s \"%s\"", TR_8PTR(pcl), TR_8STR(pv)
3112  px = dk4str8_next(pv, NULL);
3113  if (NULL == px) {
3114    switch ( (res = dk4str8_array_index(deny_action_names, pv, 0)) ) {
3115      case 0: case 1: {
3116        if (0 == (2 & pcl->hdl)) {
3117	  pcl->da  =  res;
3118	  pcl->hdl |= 2;
3119	  back	   =  1;
3120	} else {					$? "! redefinition"
3121	  /* ERROR: Redefinition of deny action */
3122          log_1(fn, lineno, LOG_EMERG, 1, 35);
3123	}
3124      } break;
3125      default: {
3126      } break;
3127    }
3128  } else {						$? "! unexpected text"
3129    /* ERROR: Syntax, no further text expected */
3130    log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3131  }
3132  $? "- set_deny_action %d", back
3133  return back;
3134}
3135
3136
3137
3138/**	Add an alias for the current printer to list.
3139	@param	ppr	Printer to add alias for.
3140	@param	pv	Alias name.
3141	@return	1 on success, 0 on error.
3142*/
3143static
3144int
3145add_alias(pqd_printer_t *ppr, char *pv, const char *fn, unsigned long lineno)
3146{
3147  pqd_printer_alias_t	*ppa;
3148  pqd_printer_t		*pp;
3149  char			*px;
3150  int			 back	= 0;
3151  $? "+ add_alias %s \"%s\"", TR_8PTR(ppr), TR_8STR(pv)
3152  px = dk4str8_next(pv, NULL);
3153  if (NULL == px) {
3154    ppa = (pqd_printer_alias_t *)dk4sto_it_find_like(conf.i_a, pv, 1);
3155    if (NULL == ppa) {
3156      pp = (pqd_printer_t *)dk4sto_it_find_like(conf.i_p, pv, 1);
3157      if (NULL == pp) {
3158        ppa = printer_alias_new(pv, ppr);
3159	if (NULL != ppa) {
3160	  if (0 != dk4sto_add(conf.s_a, ppa, NULL)) {
3161	    back = 1;
3162	  } else {				$? "! allocation (2)"
3163	    /* ERROR: Memory */
3164	    log_1(fn, lineno, LOG_EMERG, 1, 27);
3165	    printer_alias_delete(ppa);
3166	  }
3167	} else {				$? "! allocation (1)"
3168	  /* ERROR: Memory */
3169	  log_1(fn, lineno, LOG_EMERG, 1, 27);
3170	}
3171      } else {					$? "! printer exists"
3172        /* ERROR: Printer exists */
3173        log_3(fn, lineno, LOG_EMERG, 1, 36, 37, pv);
3174      }
3175    } else {					$? "! alias exists"
3176      /* ERROR: Alias already exists */
3177      log_3(fn, lineno, LOG_EMERG, 1, 38, 39, pv);
3178    }
3179  } else {					$? "! unexpected text"
3180    /* ERROR: Syntax, unexpected text */
3181    log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3182  }
3183  $? "- add_alias %d", back
3184  return back;
3185}
3186
3187
3188
3189/**	Assign a class to a printer.
3190	@param	ppr	Printer to modify.
3191	@param	pv	Class name.
3192	@return	1 on success, 0 on error.
3193*/
3194static
3195int
3196assign_class(pqd_printer_t *ppr, char *pv, const char *fn, unsigned long lineno)
3197{
3198  pqd_class_t	*pcl;
3199  char		*px;
3200  int		 back	= 0;
3201  $? "+ assign_class %s \"%s\"", TR_8PTR(ppr), TR_8STR(pv)
3202  px = dk4str8_next(pv, NULL);
3203  if (NULL == px) {
3204    if (NULL == ppr->cl) {
3205      pcl = (pqd_class_t *)dk4sto_it_find_like(conf.i_c, pv, 1);
3206      if (NULL != pcl) {
3207        ppr->cl = pcl;
3208	back = 1;
3209      } else {						$? "! not found"
3210        /* ERROR: Syntax, class not found */
3211	log_3(fn, lineno, LOG_EMERG, 1, 40, 41, pv);
3212      }
3213    } else {						$? "! redefinition"
3214      /* ERROR: Syntax, redefinition of printer class */
3215      log_1(fn, lineno, LOG_EMERG, 1, 42);
3216    }
3217  } else {						$? "! unexpected text"
3218    /* ERROR: Syntax, unexpected text */
3219    log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3220  }
3221  $? "- assign_class %d", back
3222  return back;
3223}
3224
3225
3226
3227/**	Check configuration for errors.
3228	@return	1 if configuration is usable, 0 on errors.
3229*/
3230static
3231int
3232config_check(const char *filename, unsigned long lineno)
3233{
3234  pqd_printer_t	*pr	= NULL;
3235  pqd_class_t	*cl	= NULL;
3236  int		 back	= 1;
3237
3238  $? "+ config_check"
3239  /*	Each printer must have a class assigned.
3240  */
3241  dk4sto_it_reset(conf.i_p);
3242  do {
3243    pr = (pqd_printer_t *)dk4sto_it_next(conf.i_p);
3244    if (NULL != pr) {
3245      if (NULL == pr->cl) {
3246        back = 0; 	$? "! no class for printer \"%s\"", TR_8STR(pr->name)
3247	/* ERROR: No class for printer */
3248	log_3(filename, lineno, LOG_EMERG, 1, 91, 92, pr->name);
3249      }
3250    }
3251  } while(NULL != pr);
3252
3253  /*	Each class should have a default limit
3254  */
3255  dk4sto_it_reset(conf.i_c);
3256  do {
3257    cl = (pqd_class_t *)dk4sto_it_next(conf.i_c);
3258    if (NULL != cl) {
3259      if (0 == cl->hdl) {
3260        $? "! WARNING no default limit for class \"%s\"", TR_8STR(cl->name)
3261        /* WARNING: Default limit missing for class */
3262	log_3(filename, lineno, LOG_EMERG, 0, 93, 94, cl->name);
3263      }
3264    }
3265  } while (NULL != cl);
3266
3267  $? "- config_check %d", back
3268  return back;
3269}
3270
3271
3272
3273/**	Set additional features to log.
3274	@param	pv	Text containing the feature names.
3275	@return	1 on success, 0 on errors.
3276*/
3277static
3278int
3279set_log_features(char *pv)
3280{
3281
3282  char		*pc;
3283  char		*pn;
3284  int		 back	= 0;
3285
3286  if (NULL != pv) {
3287    back = 1;
3288    pc = dk4str8_start(pv, NULL);
3289    while (NULL != pc) {
3290      pn = strchr(pc, ',');
3291      if (NULL != pn) {
3292        *(pn++) = '\0';
3293	pn = dk4str8_start(pn, NULL);
3294      }
3295      dk4str8_normalize(pc, NULL);
3296      switch ( dk4str8_array_index(log_feature_names, pc, 0) ) {
3297        case 0 : {
3298	  conf.ldb = 1;
3299	} break;
3300	case 1 : {
3301	  conf.linf = 1;
3302	} break;
3303	case 2 : case 3 : {
3304	  conf.ldb = 1;
3305	  conf.linf = 1;
3306	} break;
3307	case 4 : case 5 : {
3308	  conf.ldb = 0;
3309	  conf.linf = 0;
3310	} break;
3311	default : {
3312	  /* ERROR: Unknown log feature name */
3313	  log_3(NULL, 0UL, LOG_EMERG, 0, 154, 155, pc);
3314	  back = 0;
3315	} break;
3316      }
3317      pc = pn;
3318    }
3319  } else {
3320    /* ERROR: Value required */
3321    conf.ldb = 0; conf.linf = 0;
3322  }
3323  return back;
3324}
3325
3326
3327
3328/**	Read configuration file.
3329	@return	1 on success (can continue), 0 on error (must exit).
3330*/
3331static
3332int
3333config_read(void)
3334{
3335  const	char		*fn	= NULL;	/* Input file name */
3336  FILE			*fipo	= NULL;	/* Input file */
3337  pqd_class_t		*pcl	= NULL;	/* Current class to modify */
3338  pqd_printer_t		*ppr	= NULL;	/* Current printer to modify */
3339  pqd_printer_alias_t	*ppa	= NULL;	/* Printer alias */
3340  char			*pk	= NULL;	/* Key text */
3341  char			*pv	= NULL;	/* Value text */
3342  char			*px	= NULL;	/* Text after value */
3343  unsigned long		 lineno	= 0UL;	/* Current line number */
3344  int			 back	= 0;	/* Result */
3345  int			 sectt	= 0;	/* Section type: 0=opt, 1=class, 2=pr */
3346  int			 myres	= 0;	/* Internal operation result */
3347  $? "+ config_read"
3348  if (0 != config_allocate()) {
3349    if (0 != options[1].found) { fn = options[1].val.t; }
3350    if (NULL == fn) { fn = def_conf_file_name; }
3351    fipo = dk4fopen_c8(fn, printqd_kw[1], DK4_FOPEN_SC_IS_REGULAR, NULL);
3352    if (NULL != fipo) {
3353      back	= 1;
3354      sectt	= 0;
3355      while ((1 == back) && (NULL != fgets(inbuf, sizeof(inbuf), fipo))) {
3356        lineno++;
3357        dk4str8_delnl(inbuf);			$? ". line = \"%s\"", inbuf
3358	pk = dk4str8_start(inbuf, NULL);
3359	if (NULL != pk) {
3360	  if ('#' != *pk) {
3361	    if ('[' == *pk) {			$? ". section line"
3362	      /*
3363		Opening a new section.
3364	      */
3365	      pk = dk4str8_start(++pk, NULL);
3366	      if (NULL != pk) {			$? ". section \"%s\"", pk
3367	        sectt = -1;
3368	        pcl   = NULL;
3369	        ppr   = NULL;
3370	        pv = dk4str8_chr(pk, ']');
3371	        if (NULL != pv) { *pv = '\0'; }	$? ". section = \"%s\"", pk
3372	        pv = dk4str8_next(pk, NULL);
3373	        switch ( dk4str8_array_index(config_section_names, pk, 0) ) {
3374	          /*	Opening options section.
3375		  */
3376	          case 0 : {			$? ". options section"
3377		    sectt = 0;
3378		  } break;
3379		  /*	Opening class section.
3380		  */
3381		  case 1 : {			$? ". class section"
3382		    sectt = 1;
3383		    if (NULL != pv) {
3384		      if (NULL == (px = dk4str8_next(pv, NULL))) {
3385		        pcl = (pqd_class_t *)dk4sto_it_find_like(
3386			  conf.i_c, pv, 1
3387			);
3388		        if (NULL == pcl) {
3389		          pcl = class_new(pv);
3390			  if (NULL != pcl) {
3391			    if (0 == dk4sto_add(conf.s_c, pcl, NULL)) {
3392			      /* ERROR: Memory */
3393			      log_1(fn, lineno, LOG_EMERG, 1, 27);
3394			      class_delete(pcl);
3395			      pcl = NULL;	$? "! memory"
3396			      back = 0;
3397			    }
3398			  } else {		$? "! memory"
3399			    /* ERROR: MEMORY */
3400			    log_1(fn, lineno, LOG_EMERG, 1, 27);
3401			    back = 0;
3402			  }
3403		        } else {		$? "! already exists"
3404		          /* ERROR: Syntax, class already exists */
3405			  log_3(fn, lineno, LOG_EMERG, 1, 43, 44, pv);
3406		          back = 0;
3407		        }
3408		      } else {			$? "! unexpected text"
3409		        /* ERROR: Syntax, only one component allowed */
3410			log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3411		        back = 0;
3412		      }
3413		    } else {			$? "! missing class name"
3414		      /* ERROR: Syntax, missing class name */
3415		      log_1(fn, lineno, LOG_EMERG, 1, 45);
3416		      back = 0;
3417		    }
3418		  } break;
3419		  /*	Opening printer section.
3420		  */
3421		  case 2 : {			$? ". printer section"
3422		    sectt = 2;
3423		    if (NULL != pv) {
3424		      if (NULL == (px = dk4str8_next(pv, NULL))) {
3425		        ppr = (pqd_printer_t *)dk4sto_it_find_like(
3426			  conf.i_p,pv,1
3427			);
3428		        ppa = (pqd_printer_alias_t *)dk4sto_it_find_like(
3429		          conf.i_a,pv,1
3430		        );
3431		        if ((NULL == ppr) && (NULL == ppa)) {
3432		          ppr = printer_new(pv);
3433			  if (NULL != ppr) {
3434			    if (0 == dk4sto_add(conf.s_p, ppr, NULL)) {
3435			      /* ERROR: Memory */
3436			      log_1(fn, lineno, LOG_EMERG, 1, 27);
3437			      printer_delete(ppr);
3438			      ppr = NULL;
3439			      back = 0;		$? "! memory"
3440			    }
3441			  } else {		$? "! memory"
3442			    /* ERROR: Memory */
3443			    log_1(fn, lineno, LOG_EMERG, 1, 27);
3444			    back = 0;
3445			  }
3446		        } else {		$? "! already exists"
3447		          /* ERROR: Syntax, pr/al already exists */
3448			  log_3(fn, lineno, LOG_EMERG, 1, 46, 47, pv);
3449		          back = 0;
3450		        }
3451		      } else {			$? "! unexpected text"
3452		        /* ERROR: Syntax, just one component allowed */
3453			log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3454		        back = 0;
3455		      }
3456		    } else {			$? "! missing printer name"
3457		      /* ERROR: Syntax, name required */
3458		      log_1(fn, lineno, LOG_EMERG, 1, 48);
3459		      back = 0;
3460		    }
3461		  } break;
3462		  default : {			$? "! illegal section name"
3463		    /* ERROR: Syntax, illegal section name */
3464		    log_3(fn, lineno, LOG_EMERG, 1, 49, 50, pk);
3465		    back = 0;
3466		  } break;
3467	        }
3468	      } else {				$? "! empty section"
3469	        /* ERROR: Syntax, empty section */
3470		log_1(fn, lineno, LOG_EMERG, 1, 51);
3471	        back = 0;
3472	      }
3473	    } else {				$? ". data line"
3474	      /*
3475		Data line for existing section.
3476	      */
3477	      pv = dk4str8_chr(pk, '=');
3478	      if (NULL != pv) {
3479	        *(pv++) = '\0';
3480		pv = dk4str8_start(pv, NULL);
3481		if (NULL != pv) {
3482		  dk4str8_normalize(pk, NULL);	$? ". pk=\"%s\" pv=\"%s\"",pk,pv
3483		  switch ( dk4str8_array_index(config_option_names, pk, 0) ) {
3484		    case 0 : {
3485		      $? ". run as user"
3486		      if (0 == sectt) {
3487		        struct passwd *pw;
3488		        if (NULL == (px = dk4str8_next(pv, NULL))) {
3489		          pw = getpwnam(pv);
3490			  if (NULL != pw) {
3491			    if ((0 == conf.uid) || (conf.uid == pw->pw_uid)) {
3492			      conf.uid = pw->pw_uid;
3493			    } else {
3494			      /* ERROR: Redefinition */
3495			      log_1(fn, lineno, LOG_EMERG, 1, 52);
3496			      back = 0;		$? "! redefinition"
3497			    }
3498			  } else {		$? "! user not found"
3499			    /* ERROR: User not found */
3500			    log_3(fn, lineno, LOG_EMERG, 1, 53, 54, pv);
3501			    back = 0;
3502			  }
3503		        } else {		$? "! unexpected text"
3504		          /* ERROR: Syntax, just one component allowed */
3505			  log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3506			  back = 0;
3507		        }
3508		      } else {			$? "! only in options"
3509		        /* ERROR: Only allwowed in options section */
3510			log_1(fn, lineno, LOG_EMERG, 1, 55);
3511		        back = 0;
3512		      }
3513		    } break;
3514		    case 1 : { $? ". run as group"
3515		      if (0 == sectt) {
3516		        struct group	*pgr;
3517			if (NULL == (px = dk4str8_next(pv, NULL))) {
3518			  pgr = getgrnam(pv);
3519			  if (NULL != pgr) {
3520			    if ((0 == conf.gid) || (conf.gid == pgr->gr_gid)) {
3521			      conf.gid = pgr->gr_gid;
3522			    } else {
3523			      /* ERROR: Syntax, group redefinition */
3524			      log_1(fn, lineno, LOG_EMERG, 1, 56);
3525			      back = 0;		$? "! redefinition"
3526			    }
3527			  } else {		$? "! not found"
3528			    /* ERROR: Syntax, group not found */
3529			    log_3(fn, lineno, LOG_EMERG, 1, 57, 58, pv);
3530			    back = 0;
3531			  }
3532			} else {		$? "! unexpected text"
3533			  /* ERROR: Syntax, just one component allowed */
3534			  log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3535			  back = 0;
3536			}
3537		      } else {			$? "! only in options"
3538		        /* ERROR: Syntax, only allowed in options */
3539			log_1(fn, lineno, LOG_EMERG, 1, 55);
3540		        back = 0;
3541		      }
3542		    } break;
3543		    case 2 : { $? ". database"
3544		      if (0 == sectt) {
3545		        if (NULL == conf.dname) {
3546			  conf.dname = dk4str8_dup(pv, NULL);
3547			  if (NULL == conf.dname) {
3548			    /* ERROR: Memory */
3549			    log_1(fn, lineno, LOG_EMERG, 1, 27);
3550			    back = 0;		$? "! memory"
3551			  }
3552			} else {
3553			  /* ERROR: Redefinition */
3554			  log_1(fn, lineno, LOG_EMERG, 1, 59);
3555			  back = 0;		$? "! redefinition"
3556			}
3557		      } else {			$? "! only in options"
3558		        /* ERROR: Syntax, only allowed in options */
3559			log_1(fn, lineno, LOG_EMERG, 1, 55);
3560		        back = 0;
3561		      }
3562		    } break;
3563		    case 3 : { $? ". local socket"
3564		      if (0 == sectt) {
3565		        if (NULL == conf.sname) {
3566			  conf.sname = dk4str8_dup(pv, NULL);
3567			  if (NULL == conf.sname) {
3568			    /* ERROR: Memory */
3569			    log_1(fn, lineno, LOG_EMERG, 1, 27);
3570			    back = 0;		$? "! memory"
3571			  }
3572			} else {		$? "! redefinition"
3573			  /* ERROR: Redefinition */
3574			  log_1(fn, lineno, LOG_EMERG, 1, 60);
3575			  back = 0;
3576			}
3577		      } else {			$? "! only in options"
3578		        /* ERROR: Syntax, only allowed in options */
3579			log_1(fn, lineno, LOG_EMERG, 1, 55);
3580		        back = 0;
3581		      }
3582		    } break;
3583		    case 4 : { $? ". local socket backlog"
3584		      if (0 == sectt) {
3585		        if (0 == set_backlog(&(conf.slbl), pv, fn, lineno)) {
3586			  back = 0;		$? "! failed"
3587			}
3588		      } else {			$? "! only in options"
3589		        /* ERROR: Syntax, only allowed in options */
3590			log_1(fn, lineno, LOG_EMERG, 1, 55);
3591		        back = 0;
3592		      }
3593		    } break;
3594		    case 5 : { $? ". max local connections"
3595		      if (0 == sectt) {
3596		        if(0 == set_max_connections(&(conf.m_loc),pv,fn,lineno))
3597			{
3598			  back = 0;		$? "! failed"
3599			}
3600		      } else {			$? "! only in options"
3601		        /* ERROR: Syntax, only allowed in options */
3602			log_1(fn, lineno, LOG_EMERG, 1, 55);
3603		        back = 0;
3604		      }
3605		    } break;
3606		    case 6 : { $? ". udp port"
3607		      if (0 == sectt) {
3608		        if (0 == set_port_number(&(conf.pudp),pv,fn,lineno)) {
3609			  back = 0;		$? "! failed"
3610			}
3611		      } else {			$? "! only in options"
3612		        /* ERROR: Syntax, only allowed in options */
3613			log_1(fn, lineno, LOG_EMERG, 1, 55);
3614		        back = 0;
3615		      }
3616		    } break;
3617		    case 7 : { $? ". tcp port"
3618		      if (0 == sectt) {
3619		        if (0 == set_port_number(&(conf.ptcp),pv,fn,lineno)) {
3620			  back = 0;		$? "! failed"
3621			}
3622		      } else {			$? "! only in options"
3623		        /* ERROR: Syntax, only allowed in options */
3624			log_1(fn, lineno, LOG_EMERG, 1, 55);
3625		        back = 0;
3626		      }
3627		    } break;
3628		    case 8 : { $? ". tcp port backlog"
3629		      if (0 == sectt) {
3630		        if (0 == set_backlog(&(conf.snbl), pv, fn, lineno)) {
3631			  back = 0;		$? "! failed"
3632			}
3633		      } else {			$? "! only in options"
3634		        /* ERROR: Syntax, only allowed in options */
3635			log_1(fn, lineno, LOG_EMERG, 1, 55);
3636		        back = 0;
3637		      }
3638		    } break;
3639		    case 9 : { $? ". max tcp connections"
3640		      if (0 == sectt) {
3641		        if(0 == set_max_connections(&(conf.m_tcp),pv,fn,lineno))
3642			{
3643			  back = 0;		$? "! failed"
3644			}
3645		      } else {			$? "! only in options"
3646		        /* ERROR: Syntax, only allowed in options */
3647			log_1(fn, lineno, LOG_EMERG, 1, 55);
3648		        back = 0;
3649		      }
3650		    } break;
3651		    case 10 : { $? ". info allow"
3652		      if (0 == sectt) {
3653			myres = allow_hosts(
3654			  conf.s_ai, &(conf.iglo), pv,
3655			  fn, lineno
3656			);
3657			if (0 == myres) {	$? "! failed"
3658			  back = 0;
3659			}
3660		      } else {			$? "! only in options"
3661		        /* ERROR: Syntax, only allowed in options */
3662			log_1(fn, lineno, LOG_EMERG, 1, 55);
3663		        back = 0;
3664		      }
3665		    } break;
3666		    case 11 : { $? ". data allow"
3667		      if (0 == sectt) {
3668			myres = allow_hosts(
3669			  conf.s_ad, &(conf.dglo), pv,
3670			  fn, lineno
3671			);
3672			if (0 == myres) {	$? "! failed"
3673			  back = 0;
3674			}
3675		      } else {			$? "! only in options"
3676		        /* ERROR: Syntax, only allowed in options */
3677			log_1(fn, lineno, LOG_EMERG, 1, 55);
3678		        back = 0;
3679		      }
3680		    } break;
3681		    case 12 : { $? ". admin allow"
3682		      if (0 == sectt) {
3683			myres = allow_hosts(
3684			  conf.s_aa, &(conf.aglo), pv,
3685			  fn, lineno
3686			);
3687			if (0 == myres) {	$? "! failed"
3688			  back = 0;
3689			}
3690		      } else {			$? "! only in options"
3691		        /* ERROR: Syntax, only allowed in options */
3692			log_1(fn, lineno, LOG_EMERG, 1, 55);
3693		        back = 0;
3694		      }
3695		    } break;
3696		    case 13 : { $? ". user limit"
3697		      if ((1 == sectt) && (NULL != pcl)) {
3698			myres = add_limit(
3699			  pcl->s_u, pcl->i_u, pv, fn, lineno
3700			);
3701			if (0 == myres) {	$? "! failed"
3702			  back = 0;
3703			}
3704		      } else {			$? "! only in class"
3705		        /* ERROR: Syntax, only allowed in class */
3706			log_1(fn, lineno, LOG_EMERG, 1, 61);
3707		        back = 0;
3708		      }
3709		    } break;
3710		    case 14 : { $? ". group limit"
3711		      if ((1 == sectt) && (NULL != pcl)) {
3712			myres = add_limit(
3713			  pcl->s_g, pcl->i_g, pv, fn, lineno
3714			);
3715			if (0 == myres) {	$? "! failed"
3716			  back = 0;
3717			}
3718		      } else {			$? "! only in class"
3719		        /* ERROR: Syntax, only allowed in class */
3720			log_1(fn, lineno, LOG_EMERG, 1, 61);
3721		        back = 0;
3722		      }
3723		    } break;
3724		    case 15 : { $? ". default limit"
3725		      if ((1 == sectt) && (NULL != pcl)) {
3726			myres = set_default_limit(pcl, pv, fn, lineno);
3727			if (0 == myres) {	$? "! failed"
3728			  back = 0;
3729			}
3730		      } else {			$? "! only in class"
3731		        /* ERROR: Syntax, only allowed in class */
3732			log_1(fn, lineno, LOG_EMERG, 1, 61);
3733		        back = 0;
3734		      }
3735		    } break;
3736		    case 16 : { $? ". deny action"
3737		      if ((1 == sectt) && (NULL != pcl)) {
3738			myres = set_deny_action(pcl, pv, fn, lineno);
3739			if (0 == myres) {	$? "! failed"
3740			  back = 0;
3741			}
3742		      } else {			$? "! only in class"
3743		        /* ERROR: Syntax, only allowed in class */
3744			log_1(fn, lineno, LOG_EMERG, 1, 61);
3745		        back = 0;
3746		      }
3747		    } break;
3748		    case 17 : { $? ". alias"
3749		      if ((2 == sectt) && (NULL != ppr)) {
3750			myres = add_alias(ppr, pv, fn, lineno);
3751			if (0 == myres) { 	$? "! failed"
3752			  back = 0;
3753			}
3754		      } else {			$? "! only in printer"
3755		        /* ERROR: Syntax, only allowed in printer */
3756			log_1(fn, lineno, LOG_EMERG, 1, 62);
3757		        back = 0;
3758		      }
3759		    } break;
3760		    case 18 : { $? ". class"
3761		      if ((2 == sectt) && (NULL != ppr)) {
3762		        myres = assign_class(ppr, pv, fn, lineno);
3763			if (0 == myres) {	$? "! failed"
3764			  back = 0;
3765			}
3766		      } else {			$? "! only in printer"
3767		        /* ERROR: Syntax, only allowed in printer */
3768			log_1(fn, lineno, LOG_EMERG, 1, 62);
3769		        back = 0;
3770		      }
3771		    } break;
3772		    case 19 : {	$? ". log file"
3773		      if (0 == sectt) {
3774		        if (NULL == conf.lname) {
3775			  conf.lname = dk4str8_dup(pv, NULL);
3776			  if (NULL == conf.lname) {
3777			    /* ERROR: Memory */
3778			    log_1(fn, lineno, LOG_EMERG, 1, 27);
3779			    back = 0;		$? "! memory"
3780			  }
3781			} else {		$? "! redefinition"
3782			  /* ERROR: Redefinition */
3783			  log_1(fn, lineno, LOG_EMERG, 1, 63);
3784			  back = 0;
3785			}
3786		      } else {			$? "! only in options"
3787		        /* ERROR: Only in options */
3788			log_1(fn, lineno, LOG_EMERG, 1, 55);
3789		        back = 0;
3790		      }
3791		    } break;
3792		    case 20 : {	$? ". log features"
3793		      back = set_log_features(pv);
3794		    } break;
3795		    case 21 : {	$? ". local socket owner"
3796		      if (0 == sectt) {
3797		        struct passwd *pw;
3798		        if (NULL == (px = dk4str8_next(pv, NULL))) {
3799		          pw = getpwnam(pv);
3800			  if (NULL != pw) {
3801			    if ((0 == conf.suid) || (conf.suid == pw->pw_uid)) {
3802			      conf.suid = pw->pw_uid;
3803			    } else {
3804			      /* ERROR: Redefinition */
3805			      log_1(fn, lineno, LOG_EMERG, 1, 52);
3806			      back = 0;		$? "! redefinition"
3807			    }
3808			  } else {		$? "! user not found"
3809			    /* ERROR: User not found */
3810			    log_3(fn, lineno, LOG_EMERG, 1, 53, 54, pv);
3811			    back = 0;
3812			  }
3813		        } else {		$? "! unexpected text"
3814		          /* ERROR: Syntax, just one component allowed */
3815			  log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3816			  back = 0;
3817		        }
3818		      } else {			$? "! only in options"
3819		        /* ERROR: Only allwowed in options section */
3820			log_1(fn, lineno, LOG_EMERG, 1, 55);
3821		        back = 0;
3822		      }
3823		    } break;
3824		    case 22 : {	$? ". local socket group"
3825		      if (0 == sectt) {
3826		        struct group	*pgr;
3827			if (NULL == (px = dk4str8_next(pv, NULL))) {
3828			  pgr = getgrnam(pv);
3829			  if (NULL != pgr) {
3830			    if((0 == conf.sgid) || (conf.sgid == pgr->gr_gid)) {
3831			      conf.sgid = pgr->gr_gid;
3832			    } else {
3833			      /* ERROR: Syntax, group redefinition */
3834			      log_1(fn, lineno, LOG_EMERG, 1, 56);
3835			      back = 0;		$? "! redefinition"
3836			    }
3837			  } else {		$? "! not found"
3838			    /* ERROR: Syntax, group not found */
3839			    log_3(fn, lineno, LOG_EMERG, 1, 57, 58, pv);
3840			    back = 0;
3841			  }
3842			} else {		$? "! unexpected text"
3843			  /* ERROR: Syntax, just one component allowed */
3844			  log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px);
3845			  back = 0;
3846			}
3847		      } else {			$? "! only in options"
3848		        /* ERROR: Syntax, only allowed in options */
3849			log_1(fn, lineno, LOG_EMERG, 1, 55);
3850		        back = 0;
3851		      }
3852		    } break;
3853		  }
3854		} else {			$? "! value  text missing"
3855		  /* ERROR: Syntax, missing value */
3856		  log_1(fn, lineno, LOG_EMERG, 1, 64);
3857		  back = 0;
3858		}
3859	      } else {				$? "! value text missing"
3860	        /* ERROR: Syntax, missing value */
3861		log_1(fn, lineno, LOG_EMERG, 1, 64);
3862		back = 0;
3863	      }
3864	    }
3865	  }
3866#if TRACE_DEBUG
3867	  else {				$? ". comment line"
3868	  }
3869#endif
3870	}
3871#if TRACE_DEBUG
3872	else {					$? ". empty line"
3873	}
3874#endif
3875      }
3876      fclose(fipo);
3877      if (1 == back) {
3878        back = config_check(fn, lineno);
3879      }
3880    } else {					$? "! failed to open file"
3881      /* ERROR: Failed to open file */
3882      log_1(fn, 0UL, LOG_EMERG, 1, 65);
3883    }
3884  } else {
3885    /* ERROR: Memory */
3886    log_1(NULL, 0UL, LOG_EMERG, 1, 27);
3887  }
3888  $? "- config_read %d", back
3889  return back;
3890}
3891
3892
3893
3894static
3895void
3896release_allowed_peers(dk4_sto_it_t *it)
3897{
3898  dk4_allowed_peer_t	*peer;
3899  $? "+ release_allowed_peers %s", TR_8PTR(it)
3900  dk4sto_it_reset(it);
3901  do {
3902    peer = (dk4_allowed_peer_t *)dk4sto_it_next(it);
3903    if (NULL != peer) {
3904      dk4mem_free(peer);
3905    }
3906  } while (NULL != peer);
3907  dk4sto_it_close(it);
3908  $? "- release_allowed_peers"
3909}
3910
3911
3912
3913/**	Release resources used to save configuration data.
3914*/
3915static
3916void
3917config_release(void)
3918{
3919  $? "+ config_release"
3920  prev_uid = conf.uid;
3921  prev_gid = conf.gid;
3922
3923  if (NULL != conf.i_aa) {	/* Allowed peers for administration */
3924    release_allowed_peers(conf.i_aa);
3925  } conf.i_aa = NULL;
3926  if (NULL != conf.s_aa) {
3927    dk4sto_close(conf.s_aa);
3928  } conf.s_aa = NULL;
3929  if (NULL != conf.i_ad) {	/* Allowed peers for data */
3930    release_allowed_peers(conf.i_ad);
3931  } conf.i_ad = NULL;
3932  if (NULL != conf.s_ad) {
3933    dk4sto_close(conf.s_ad);
3934  } conf.s_ad = NULL;
3935  if (NULL != conf.i_ai) {	/* Allowed peers for information */
3936    release_allowed_peers(conf.i_ai);
3937  } conf.i_ai = NULL;
3938  if (NULL != conf.s_ai) {
3939    dk4sto_close(conf.s_ai);
3940  } conf.s_ai = NULL;
3941  if (NULL != conf.i_a) {	/* Aliases */
3942    printer_alias_all_delete(conf.i_a);
3943  } conf.i_a = NULL;
3944  if (NULL != conf.s_a) {
3945    dk4sto_close(conf.s_a);
3946  } conf.s_a = NULL;
3947  if (NULL != conf.i_p) {	/* Printers */
3948    printer_all_delete(conf.i_p);
3949  } conf.i_p = NULL;
3950  if (NULL != conf.s_p) {
3951    dk4sto_close(conf.s_p);
3952  } conf.s_p = NULL;
3953  if (NULL != conf.i_c) {	/* Classes */
3954    class_all_delete(conf.i_c);
3955  } conf.i_c = NULL;
3956  if (NULL != conf.s_c) {
3957    dk4sto_close(conf.s_c);
3958  } conf.s_c = NULL;
3959  dk4mem_release(conf.dname);
3960  dk4mem_release(conf.sname);
3961  dk4mem_release(conf.lname);
3962  conf.m_loc = 0;
3963  conf.m_tcp = 0;
3964  conf.uid   = 0;
3965  conf.gid   = 0;
3966  conf.slbl  = 0;
3967  conf.snbl  = 0;
3968  conf.ptcp  = 9100;
3969  conf.pudp  = 9100;
3970  $? "- config_release"
3971}
3972
3973
3974
3975/**	Allocate resources before starting outer loop.
3976	@return	1 on success (can enter loop), 0 on error (exit).
3977*/
3978static
3979int
3980outer_allocate_resources(void)
3981{
3982  int		 back	= 0;
3983  $? "+ outer_allocate_resources"
3984
3985  /*	Initialize variables to NULL.
3986  */
3987  s_c_unix = NULL;
3988  i_c_unix = NULL;
3989  s_c_tcp  = NULL;
3990  i_c_tcp  = NULL;
3991
3992  /*	Allocate resources.
3993  */
3994  s_c_unix = dk4sto_open(NULL);
3995  if (NULL == s_c_unix) {			$? "! allocation (1)"
3996    /* ERROR: Memory */
3997    log_1(NULL, 0UL, LOG_EMERG, 1, 27);
3998    goto finished;
3999  }
4000  dk4sto_set_comp(s_c_unix, compare_pqd_l_conn_t, 0);
4001  i_c_unix = dk4sto_it_open(s_c_unix, NULL);
4002  if (NULL == i_c_unix) {			$? "! allocation (2)"
4003    /* ERROR: Memory */
4004    log_1(NULL, 0UL, LOG_EMERG, 1, 27);
4005    goto finished;
4006  }
4007  s_c_tcp = dk4sto_open(NULL);
4008  if (NULL == s_c_tcp) {				$? "! allocation (3)"
4009    /* ERROR: Memory */
4010    log_1(NULL, 0UL, LOG_EMERG, 1, 27);
4011    goto finished;
4012  }
4013  dk4sto_set_comp(s_c_tcp, compare_pqd_n_conn_t, 0);
4014  i_c_tcp = dk4sto_it_open(s_c_tcp, NULL);
4015  if (NULL == i_c_tcp) {				$? "! allocation (4)"
4016    /* ERROR: Memory */
4017    log_1(NULL, 0UL, LOG_EMERG, 1, 27);
4018    goto finished;
4019  }
4020
4021  /*	Indicate success.
4022  */
4023  back = 1;
4024
4025  finished:
4026  $? "- outer_allocate_resources %d", back
4027  return back;
4028}
4029
4030
4031
4032/**	Release resources after finishing outer loop.
4033*/
4034static
4035void
4036outer_release_resources(void)
4037{
4038  pqd_n_conn_t	*pnc;
4039  pqd_l_conn_t	*plc;
4040  $? "+ outer_release_resources"
4041
4042  if (NULL != i_c_tcp) {
4043    dk4sto_it_reset(i_c_tcp);
4044    do {
4045      pnc = (pqd_n_conn_t *)dk4sto_it_next(i_c_tcp);
4046      if (NULL != pnc) {
4047        /* Release pnc */
4048	if (INVALID_SOCKET != pnc->sock) {
4049	  (void)dk4socket_close(pnc->sock, NULL);
4050	}
4051	dk4mem_free(pnc);
4052      }
4053    } while(NULL != pnc);
4054    dk4sto_it_close(i_c_tcp);
4055  }
4056  i_c_tcp = NULL;
4057  if (NULL != s_c_tcp) {
4058    dk4sto_close(s_c_tcp);
4059  }
4060  s_c_tcp = NULL;
4061  if (NULL != i_c_unix) {
4062    dk4sto_it_reset(i_c_unix);
4063    do {
4064      plc = (pqd_l_conn_t *)dk4sto_it_next(i_c_unix);
4065      if (NULL != plc) {
4066        /* Release plc */
4067	if (INVALID_SOCKET != plc->sock) {
4068	  (void)dk4socket_close(plc->sock, NULL);
4069	}
4070	dk4mem_free(plc);
4071      }
4072    } while (NULL != plc);
4073    dk4sto_it_close(i_c_unix);
4074  }
4075  i_c_unix = NULL;
4076  if (NULL != s_c_unix) {
4077    dk4sto_close(s_c_unix);
4078  }
4079  s_c_unix = NULL;
4080  $? "- outer_release_resources"
4081}
4082
4083
4084
4085static
4086void
4087pqd_dbi_open_error(const char *fn, dk4_er_t *erp)
4088{
4089  switch (erp->ec) {
4090    case DK4_E_NOT_SUPPORTED : {
4091      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 202, fn);
4092    } break;
4093    case DK4_E_MATH_OVERFLOW : {
4094      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 203, fn);
4095    } break;
4096    case DK4_E_MEMORY_ALLOCATION_FAILED : {
4097      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 204, fn);
4098    } break;
4099    case DK4_E_SYNTAX : {
4100      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 205, fn);
4101    } break;
4102    case DK4_E_SEC_CHECK : {
4103      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 206, fn);
4104    } break;
4105    case DK4_E_OPEN_READ_FAILED : {
4106      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 207, fn);
4107    } break;
4108    case DK4_E_OPEN_WRITE_FAILED : {
4109      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 208, fn);
4110    } break;
4111    case DK4_E_CLOSE_FAILED : {
4112      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 209, fn);
4113    } break;
4114    default : {
4115      log_3(NULL, 0UL, LOG_EMERG, 1, 105, 106, fn);
4116    } break;
4117  }
4118}
4119
4120
4121/**	Allocate resources before starting inner loop.
4122	* Create log directory and log file.
4123	* Open database.
4124	* Log message for starting service.
4125	@return	1 on success (can enter loop), 0 on error (exit).
4126*/
4127static
4128int
4129inner_allocate_resources(void)
4130{
4131  char		 necbuf[16*sizeof(dk4_um_t)];
4132  dk4_er_t	 er;
4133  const char	*fn;
4134  uid_t		 sockuid;
4135  gid_t		 sockgid;
4136  int		 res;
4137  int		 back	= 0;
4138  $? "+ inner_allocate_resources"
4139
4140  /*	Log file directory hierarchy.
4141  */
4142  fn = conf.lname;
4143  if (NULL == fn) { fn = def_log_file_name; }
4144  if (0 != user_switched) {
4145    res = dk4mkdir_hierarchy_c8(fn, 0, NULL);
4146  } else {
4147    res = dk4mkdir_hierarchy_ugm_c8(fn, 0, conf.uid, conf.gid, 0750, NULL);
4148  }
4149  if (0 == res) {				$? "! log dir hierarchy"
4150    /* ERROR: Failed to create directory hierachy for log file */
4151    log_3(NULL, 0UL, LOG_EMERG, 1, 95, 96, fn);
4152    goto finished;
4153  }
4154
4155  /*	Database directory hierarchy
4156  */
4157  fn = conf.dname;
4158  if (NULL == fn) { fn = def_db_file_name; }
4159  fn = dk4dbi_filename_part(fn);
4160  if (0 != user_switched) {
4161    res = dk4mkdir_hierarchy_c8(fn, 0, NULL);
4162  } else {
4163    res = dk4mkdir_hierarchy_ugm_c8(fn, 0, conf.uid, conf.gid, 0750, NULL);
4164  }
4165  if (0 == res) {				$? "! db dir hierarchy"
4166    /* ERROR: Failed to create directory hierarchy for db file */
4167    log_3(NULL, 0UL, LOG_EMERG, 1, 97, 98, fn);
4168    goto finished;
4169  }
4170
4171  /*	Socket directory hierarchy
4172  */
4173  fn = conf.sname;
4174  if (NULL == fn) { fn = def_sock_file_name; }
4175  sockuid = conf.suid;
4176  if (0 == sockuid) { sockuid = conf.uid; }
4177  sockgid = conf.sgid;
4178  if (0 == sockgid) { sockgid = conf.gid; }
4179  if ((0 != user_switched) && (0 != conf.uid)) {
4180    res = dk4mkdir_hierarchy_c8(fn, 0, NULL);
4181  } else {
4182    /*
4183	In contrast to the db and log directories the non-privileged
4184	user needs write access to the directory as the socket is deleted
4185	and newly created.
4186    */
4187    res = dk4mkdir_hierarchy_ugm_c8(fn, 0, sockuid, sockgid, 0770, NULL);
4188  }
4189  if (0 == res) {				$? "! socket dir hierarchy"
4190    /* ERROR: Failed to create directory hierarchy for socket */
4191    log_3(NULL, 0UL, LOG_EMERG, 1, 99, 100, fn);
4192    goto finished;
4193  }
4194
4195  /*	UNIX listener socket.
4196  */
4197  fn = conf.sname;
4198  if (NULL == fn) { fn = def_sock_file_name; }
4199  $? ". create local socket \"%s\"", fn
4200  dk4error_init(&er);
4201  if (
4202    ((0 != sockuid) || (0 != sockgid))
4203    && ((0 == user_switched) || (0 == conf.uid))
4204  )
4205  {
4206    so_unix = dk4socket_c8_unix_listener_owner_group_mode(
4207      fn, ((0 < conf.slbl) ? (conf.slbl) : 5), sockuid, sockgid, 0660, &er
4208    );
4209  } else {
4210    so_unix = dk4socket_c8_unix_listener(
4211      fn, ((0 < conf.slbl) ? (conf.slbl) : 5), &er
4212    );
4213  }
4214  if (INVALID_SOCKET == so_unix) {			$? "! local socket"
4215    /* ERROR: Failed to create listener socket */
4216    res = dk4ma_write_c8_decimal_signed(
4217      necbuf, sizeof(necbuf), (dk4_im_t)(er.dt.iDetails1), 0, NULL
4218    );
4219    if (0 != res) {
4220      switch (er.ec) {
4221        case DK4_E_SYNTAX : {
4222	  log_3(NULL, 0UL, LOG_EMERG, 1, 101, 178, fn);
4223	} break;
4224	case DK4_E_NON_SOCKET : {
4225	  log_3(NULL, 0UL, LOG_EMERG, 1, 101, 179, fn);
4226	} break;
4227	case DK4_E_UNLINK_FAILED : {
4228	  log_3(NULL, 0UL, LOG_EMERG, 1, 101, 180, fn);
4229	} break;
4230	case DK4_E_SOCKET_SOCKET : {
4231	  log_5(NULL, 0UL, LOG_EMERG, 1, 101, 181, 182, fn, necbuf);
4232	} break;
4233	case DK4_E_SOCKET_BIND : {
4234	  log_5(NULL, 0UL, LOG_EMERG, 1, 101, 183, 182, fn, necbuf);
4235	} break;
4236	case DK4_E_SOCKET_LISTEN : {
4237	  log_5(NULL, 0UL, LOG_EMERG, 1, 101, 184, 182, fn, necbuf);
4238	} break;
4239	case DK4_E_CHOWN_FAILED : {
4240	  log_3(NULL, 0UL, LOG_EMERG, 1, 101, 185, fn);
4241	} break;
4242	case DK4_E_CHMOD_FAILED : {
4243	  log_3(NULL, 0UL, LOG_EMERG, 1, 101, 186, fn);
4244	} break;
4245	default : {
4246	  log_3(NULL, 0UL, LOG_EMERG, 1, 101, 102, fn);
4247	} break;
4248      }
4249    } else {
4250      log_3(NULL, 0UL, LOG_EMERG, 1, 101, 102, fn);
4251    }
4252    goto finished;
4253  }							$? ". local socket"
4254
4255  /*	TCP listener socket set.
4256  */
4257  if (0 < conf.ptcp) {
4258    dk4error_init(&er);
4259    ss_tcp = dk4socket_c8_set_server(
4260      conf.ptcp, ((0 < conf.snbl) ? (conf.snbl) : 5), 0, &er
4261    );
4262    if (NULL == ss_tcp) {				$? "! tcp socket set"
4263      /* ERROR: Failed to create TCP listener socket */
4264      res = dk4ma_write_c8_decimal_signed(
4265        necbuf, sizeof(necbuf), (dk4_im_t)(er.dt.iDetails1), 0, NULL
4266      );
4267      if (0 != res) {
4268        switch (er.ec) {
4269	  case DK4_E_MATH_OVERFLOW : {
4270            log_1(NULL, 0UL, LOG_EMERG, 1, 188);
4271	  } break;
4272	  case DK4_E_MEMORY_ALLOCATION_FAILED : {
4273	    log_1(NULL, 0UL, LOG_EMERG, 1, 189);
4274	  } break;
4275	  case DK4_E_SOCKET_SOCKET : {
4276	    log_3(NULL, 0UL, LOG_EMERG, 1, 190, 191, necbuf);
4277	  } break;
4278	  case DK4_E_SOCKET_BIND : {
4279	    log_3(NULL, 0UL, LOG_EMERG, 1, 192, 191, necbuf);
4280	  } break;
4281	  case DK4_E_SOCKET_LISTEN : {
4282	    log_3(NULL, 0UL, LOG_EMERG, 1, 193, 191, necbuf);
4283	  } break;
4284	  case DK4_E_SOCKET_GETADDRLOC : {
4285	    log_3(NULL, 0UL, LOG_EMERG, 1, 194, 191, necbuf);
4286	  } break;
4287	  default : {
4288            log_1(NULL, 0UL, LOG_EMERG, 1, 103);
4289	  } break;
4290	}
4291      } else {
4292        log_1(NULL, 0UL, LOG_EMERG, 1, 103);
4293      }
4294      goto finished;
4295    }
4296  }
4297
4298  /*	UDP socket set.
4299  */
4300  if (0 < conf.pudp) {
4301    dk4error_init(&er);
4302    ss_udp = dk4socket_c8_set_udp(conf.pudp, 0, 0, &er);
4303    if (NULL == ss_udp) {				$? "! udp socket set"
4304      /* ERROR: Failed to create UDP socket set */
4305      res = dk4ma_write_c8_decimal_signed(
4306        necbuf, sizeof(necbuf), (dk4_im_t)(er.dt.iDetails1), 0, NULL
4307      );
4308      if (0 != res) {
4309        switch (er.ec) {
4310	  case DK4_E_MATH_OVERFLOW : {
4311            log_1(NULL, 0UL, LOG_EMERG, 1, 195);
4312	  } break;
4313	  case DK4_E_MEMORY_ALLOCATION_FAILED : {
4314	    log_1(NULL, 0UL, LOG_EMERG, 1, 196);
4315	  } break;
4316	  case DK4_E_SOCKET_SOCKET : {
4317	    log_3(NULL, 0UL, LOG_EMERG, 1, 197, 198, necbuf);
4318	  } break;
4319	  case DK4_E_SOCKET_BIND : {
4320	    log_3(NULL, 0UL, LOG_EMERG, 1, 199, 198, necbuf);
4321	  } break;
4322	  case DK4_E_SOCKET_LISTEN : {
4323	    log_3(NULL, 0UL, LOG_EMERG, 1, 200, 198, necbuf);
4324	  } break;
4325	  case DK4_E_SOCKET_GETADDRLOC : {
4326	    log_3(NULL, 0UL, LOG_EMERG, 1, 201, 198, necbuf);
4327	  } break;
4328	}
4329      } else {
4330        log_1(NULL, 0UL, LOG_EMERG, 1, 104);
4331      }
4332      goto finished;
4333    }
4334  }
4335
4336  fn = conf.dname;
4337  if (NULL == fn) { fn = def_db_file_name; }
4338  $? ". db open attempt 1 \"%s\"", fn
4339  dk4error_init(&er);
4340  db = dk4dbi_open(fn, 1, 0, 1024, 1024, &er);
4341  if (NULL != db) {					$? ". open db (1) ok"
4342    db_modified = 0;
4343    res = 1;
4344    dk4error_init(&er);
4345    if (0 == dbi_set(db, printqd_kw[79],printqd_kw[79],&er)) { res = 0; }
4346    if (0 == dk4dbi_close(db, &er)) { res = 0; }
4347    db = NULL;
4348    db_modified = 0;
4349    if (0 != res) {
4350      $? ". db open attempt 2 \"%s\"", fn
4351      dk4error_init(&er);
4352      db = dk4dbi_open(fn, 1, 0, 1024, 1024, &er);
4353      if (NULL != db) {				$? ". open db (2) ok"
4354        db_modified = 0;
4355      } else {					$? "! open db (2)"
4356        /* ERROR: Failed to open database */
4357	pqd_dbi_open_error(fn, &er);
4358        goto finished;
4359      }
4360    } else {						$? "! initial db entry"
4361      /* ERROR: Failed to write initial entry to database */
4362      log_1(NULL, 0UL, LOG_EMERG, 1, 107);
4363      switch (er.ec) {
4364        case DK4_E_SYNTAX : {
4365	  log_1(NULL, 0UL, LOG_EMERG, 1, 210);
4366	} break;
4367	case DK4_E_NOT_SUPPORTED : {
4368	  log_1(NULL, 0UL, LOG_EMERG, 1, 211);
4369	} break;
4370	case DK4_E_MEMORY_ALLOCATION_FAILED : {
4371	  log_1(NULL, 0UL, LOG_EMERG, 1, 212);
4372	} break;
4373	case DK4_E_OPEN_WRITE_FAILED : {
4374	  log_1(NULL, 0UL, LOG_EMERG, 1, 213);
4375	} break;
4376	case DK4_E_WRITE_FAILED : {
4377	  log_1(NULL, 0UL, LOG_EMERG, 1, 214);
4378	} break;
4379	case DK4_E_CLOSE_FAILED : {
4380	  log_1(NULL, 0UL, LOG_EMERG, 1, 215);
4381	} break;
4382	case DK4_E_SEC_CHECK : {
4383	  log_1(NULL, 0UL, LOG_EMERG, 1, 216);
4384	} break;
4385	default : {
4386	  log_1(NULL, 0UL, LOG_EMERG, 1, 107);
4387	} break;
4388      }
4389      goto finished;
4390    }
4391  } else {						$? "! open db (1)"
4392    /* ERROR: Failed to open database */
4393    pqd_dbi_open_error(fn, &er);
4394    goto finished;
4395  }
4396
4397  /*	Finally indicate success.
4398  */
4399  back = 1;
4400
4401  finished:
4402  $? "- inner_allocate_resources %d", back
4403  return back;
4404}
4405
4406
4407
4408/**	Release resources after finishing inner loop.
4409	* Log message for finishing service.
4410	* Synchronize and close database.
4411	* Close log file.
4412*/
4413static
4414int
4415inner_release_resources(void)
4416{
4417  const char	*fn;
4418  int		 back	= 1;
4419  $? "+ inner_release_resources"
4420
4421  /*	Database
4422  */
4423  if (NULL != db) {
4424    if (0 == dk4dbi_close(db, NULL)) {		$? "! sync db"
4425      /* ERROR: Failed to synchronize database to disk */
4426      log_1(NULL, 0UL, LOG_EMERG, 1, 108);
4427      back = 0;
4428    }
4429    db = NULL;
4430    db_modified = 0;
4431  }
4432
4433  /*	UDP socket set.
4434  */
4435  if (NULL != ss_udp) {				$? "! udp socket set close"
4436    dk4socket_set_close(ss_udp, NULL);
4437  }
4438  ss_udp = NULL;
4439
4440  /*	TCP listener socket set.
4441  */
4442  if (NULL != ss_tcp) {				$? "! tcp socket set close"
4443    dk4socket_set_close(ss_tcp, NULL);
4444  }
4445  ss_tcp = NULL;
4446
4447  /*	Unix listener socket.
4448  */
4449  if (INVALID_SOCKET != so_unix) { dk4socket_close(so_unix, NULL); }
4450  so_unix = INVALID_SOCKET;
4451  fn = conf.sname;
4452  if (NULL == fn) { fn = def_sock_file_name; }
4453  (void)dk4delete_file_c8(fn, NULL);
4454
4455  $? "- inner_release_resources"
4456  return back;
4457}
4458
4459
4460
4461/**	Switch to non-privileged user and group if configured to do so.
4462	@return	1 on success (can continue), 0 on error (exit).
4463*/
4464static
4465int
4466change_group_and_user(void)
4467{
4468  const char	*fn	= NULL;
4469#if (!(DK4_HAVE_SETGROUPS)) && DK4_HAVE_INITGROUPS && DK4_HAVE_GETPWUID
4470  struct pwent	*pw;
4471#endif
4472  uid_t		 myuid;
4473  gid_t		 mygid;
4474  int		 dbt	= DK4_DB_BE_UNKNOWN;
4475  int		 back	= 1;
4476  $? "+ change_group_and_user"
4477  if (0 != user_switched) {			$? ". already switched"
4478    if (conf.uid != prev_uid) {
4479      back = 0;					$? "! re-switch user"
4480      /* ERROR: User to run as was changed */
4481      log_1(NULL, 0UL, LOG_EMERG, 1, 66);
4482    } else {
4483      if (conf.gid != prev_gid) {		$? "! re-switch group"
4484        back = 0;
4485	/* ERROR: Group to run as was changed */
4486	log_1(NULL, 0UL, LOG_EMERG, 1, 67);
4487      }
4488    }
4489  } else {					$? ". not yet switched"
4490    user_switched = 1;
4491    myuid = geteuid();
4492    mygid = getegid();
4493    if ((myuid != conf.uid) || (mygid != conf.gid)) {	$? ". must switch"
4494      /*
4495	  PID file ownership and permissions
4496      */
4497      if (0 != chown(pid_file_name, conf.uid, conf.gid)) {
4498        back = 0;
4499	/* ERROR: Failed to change PID file ownership */
4500        log_1(NULL, 0UL, LOG_EMERG, 1, 217);
4501      }
4502      if (0 != chmod(pid_file_name, 0660)) {
4503        back = 0;
4504	/* ERROR: Failed to change PID file permissions */
4505        log_1(NULL, 0UL, LOG_EMERG, 1, 218);
4506      }
4507      /*
4508    	  Log file ownership and permissions
4509      */
4510      fn = conf.lname;
4511      if (NULL == fn) { fn = def_log_file_name; }
4512      if (0 != chown(fn, conf.uid, conf.gid)) {
4513        back = 0;		$? "! log file ownership"
4514        /* ERROR: Failed to change log file ownership */
4515        log_1(NULL, 0UL, LOG_EMERG, 1, 109);
4516      }
4517      if (0 != chmod(fn, 0660)) {
4518        back = 0;		$? "! log file permission"
4519        /* ERROR: Failed to change log file permissions */
4520        log_1(NULL, 0UL, LOG_EMERG, 1, 110);
4521      }
4522      /*
4523	  Database ownership and permissions
4524      */
4525      if (0 == dk4dbi_change_owner_and_mode(db,conf.uid,conf.gid,0660,NULL)) {
4526        back = 0;		$? "! db ownership and permissions"
4527        /* ERROR: failed to change db ownership and permissions */
4528        log_1(NULL, 0UL, LOG_EMERG, 1, 111);
4529      }
4530      switch ( (dbt = dk4dbi_get_type(db)) ) {
4531        case DK4_DB_BE_BDB : case DK4_DB_BE_NDBM : {
4532          $? ". close database, must re-open after switching user"
4533          if (0 == dk4dbi_close(db, NULL)) {	$? "! close failed"
4534	    /* ERROR: Failed to synchronize database to disk */
4535	    log_1(NULL, 0UL, LOG_EMERG, 1, 121);
4536	  }
4537	  db = NULL;
4538	  db_modified = 0;
4539	  if (DK4_DB_BE_BDB == dbt) {
4540	    fn = ((NULL != conf.dname) ? (conf.dname) : (def_db_file_name));
4541	    fn = dk4dbi_filename_part(fn);
4542	    if (0 != chown(fn, conf.uid, conf.gid)) {
4543	      back = 0;
4544	      log_3(NULL, 0UL, LOG_EMERG, 1, 174, 175, fn);
4545	    }
4546	    if (0 != chmod(fn, 0660)) {
4547	      back = 0;
4548	      log_3(NULL, 0UL, LOG_EMERG, 1, 176, 177, fn);
4549	    }
4550	  }
4551        } break;
4552      }
4553      /*
4554	  Local UNIX socket ownership and permission
4555      */
4556      if (INVALID_SOCKET != so_unix) {		$? ". socket open"
4557        $? ". fchown socket"
4558        if (0 != fchown(so_unix, conf.uid, conf.gid)) {
4559          back = 0;	$? "! socket ownership"
4560	  /* ERROR: Failed to change socket ownership */
4561	  log_1(NULL, 0UL, LOG_EMERG, 1, 112);
4562        }
4563        $? ". fchmod socket"
4564        if (0 != fchmod(so_unix, 0660)) {
4565          back = 0;	$? "! socket permissions"
4566	  /* ERROR: Failed to change socket permissions */
4567	  log_1(NULL, 0UL, LOG_EMERG, 1, 113);
4568        }
4569      } else {					$? "! socket not yet open"
4570      }
4571#if DK4_HAVE_SETGROUPS
4572      if (0 != setgroups(1, &(conf.gid))) {
4573        back = 0;		$? "! setgroups"
4574	log_1(NULL, 0UL, LOG_EMERG, 1, 114);
4575      }
4576#else
4577#if DK4_HAVE_INITGROUPS && DK4_HAVE_GETPWUID
4578      pw = getpwuid(myuid);
4579      if (NULL != pw) {
4580        if (NULL != pw->pw_name) {
4581	  if (0 != initgroups(pw->pw_name, conf.gid)) {
4582	    back = 0;		$? "! initgroups"
4583	    log_1(NULL, 0UL, LOG_EMERG, 1, 114);
4584	  }
4585	}
4586      }
4587#endif
4588#endif
4589      /*
4590	  Switch group
4591      */
4592      if (mygid != conf.gid) {
4593        if (0 != setgid(conf.gid)) {
4594          back = 0;		$? "! switch group"
4595          /* ERROR: Failed to switch group */
4596          log_1(NULL, 0UL, LOG_EMERG, 1, 114);
4597        }
4598      }
4599      /*
4600	  Switch user
4601      */
4602      if (myuid != conf.uid) {
4603        if (0 != setuid(conf.uid)) {
4604          back = 0;		$? "! switch user"
4605          /* ERROR: Failed to switch user */
4606          log_1(NULL, 0UL, LOG_EMERG, 1, 115);
4607        }
4608      }
4609      /*
4610	  Re-open database if closed.
4611      */
4612      if (NULL == db) {	$? ". must reopen database"
4613        fn = conf.dname;
4614        if (NULL == fn) { fn = def_db_file_name; }
4615        db = dk4dbi_open(fn, 1, 0, 1024, 1024, NULL);
4616        if (NULL != db) {
4617          db_modified = 0;
4618        } else {	$? "! failed to re-open database"
4619          back = 0;
4620	  /* ERROR: Failed to open database */
4621	  log_3(NULL, 0UL, LOG_EMERG, 1, 105, 106, fn);
4622        }
4623      }
4624    } else {					$? ". user/group already ok"
4625    }
4626  }
4627  $? "- change_group_and_user %d", back
4628  return back;
4629}
4630
4631
4632
4633static
4634int
4635protocol_level_for_address(struct sockaddr *so)
4636{
4637  int		 back	= PQD_PROTO_NONE;
4638  $? "+ protocol_level_for_address"
4639  if (0 != conf.aglo) {
4640    back = PQD_PROTO_ADMIN;
4641  } else {
4642    if (NULL != dk4sto_it_find_like(conf.i_aa, so, 1)) {
4643      back = PQD_PROTO_ADMIN;
4644    } else {
4645      if (0 != conf.dglo) {
4646        back = PQD_PROTO_DATA;
4647      } else {
4648        if (NULL != dk4sto_it_find_like(conf.i_ad, so, 1)) {
4649	  back = PQD_PROTO_DATA;
4650	} else {
4651	  if (0 != conf.iglo) {
4652	    back = PQD_PROTO_INFO;
4653	  } else {
4654	    if (NULL != dk4sto_it_find_like(conf.i_ai, so, 1)) {
4655	      back = PQD_PROTO_INFO;
4656	    }
4657	  }
4658	}
4659      }
4660    }
4661  }
4662  $? "- protocol_level_for_address %d", back
4663  return back;
4664}
4665
4666
4667
4668/**	Check whether the action is allowed in the protocol level.
4669	@param	act	Action to check.
4670	@param	protlev	Protocol level.
4671	@return	1 if action is allowed, 0 otherwise.
4672*/
4673static
4674int
4675protocol_level_check(int act, int protlev)
4676{
4677  int		 back	= 0;
4678  $? "+ protocol_level_check   act=%d   protlev=%d", act, protlev
4679  switch (act) {
4680    case PQD_CMD_INFO : {
4681      if (PQD_PROTO_INFO <= protlev) { back = 1; }
4682    } break;
4683    case PQD_CMD_CONTROL : {
4684      if (PQD_PROTO_ADMIN <= protlev) { back = 1; }
4685    } break;
4686    default : {
4687      if (PQD_PROTO_DATA <= protlev) { back = 1; }
4688    } break;
4689  }
4690  $? "- protocol_level_check %d", back
4691  return back;
4692}
4693
4694
4695
4696static
4697void
4698job_init(pqd_rq_t *ptr)
4699{
4700  $? "+ job_init %s", TR_8PTR(ptr)
4701  DK4_MEMRES(ptr, sizeof(pqd_rq_t));
4702  ptr->connptr = NULL;
4703  ptr->sto     = NULL;
4704  ptr->soa     = NULL;
4705  ptr->pnconn  = NULL;
4706  ptr->cmdptr  = NULL;
4707  ptr->argptr  = NULL;
4708  ptr->szsoa   = 0;
4709  ptr->sock    = 0;
4710  ptr->action  = 0;
4711  ptr->protlev = PQD_PROTO_NONE;
4712  ptr->logthis = 1;
4713  $? "- job_init"
4714}
4715
4716
4717
4718static
4719void
4720account_init(pqd_account_t *ptr)
4721{
4722  $? "+ account_init %s", TR_8PTR(ptr)
4723  DK4_MEMRES(ptr, sizeof(pqd_account_t));
4724  ptr->limit   = (dk4_um_t)0UL;
4725  ptr->used    = (dk4_um_t)0UL;
4726  ptr->account = (dk4_um_t)0UL;
4727  ptr->da      = 0;
4728  $? "- account_init"
4729}
4730
4731
4732
4733/**	Close a stream connection record.
4734	@param	sock	Socket to close.
4735	@param	sto	Container for the records.
4736	@param	ptr	Record to delete, containing the socket.
4737	@param	pnconn	Address of connection number variable to decrease.
4738*/
4739static
4740void
4741close_stream_conn(
4742  dk4_socket_t	 sock,
4743  dk4_sto_t	*sto,
4744  void		*ptr,
4745  size_t	*pnconn
4746)
4747{
4748  $? "+ close_stream_conn %d", (int)sock
4749  if ((NULL != sto) && (NULL != ptr)) {
4750    dk4sto_remove(sto, ptr, NULL);
4751  }
4752  if (INVALID_SOCKET != sock) {
4753    (void)dk4socket_close(sock, NULL);
4754  }
4755  if (NULL != ptr) {
4756    dk4mem_free(ptr);
4757  }
4758  if (NULL != pnconn) {
4759    if (0 < *pnconn) {
4760      *pnconn -= 1;
4761    }
4762  }
4763  $? "- close_stream_conn"
4764}
4765
4766
4767
4768/**	Send a text response.
4769	@param	job	Job structure.
4770	@param	str	Text to send (still without newline).
4771*/
4772static
4773void
4774send_text(pqd_rq_t *job, const char *str)
4775{
4776  const char *	msgs[3];	/* Log message text parts */
4777  size_t	sz;		/* Number of bytes to send */
4778  size_t	wr;		/* Number of bytes successfully sent */
4779  int		res;		/* Operation result */
4780  $? "+ send_text \"%s\"", TR_8STR(str)
4781  if (0 != dk4str8_cpy_s(inbuf, sizeof(inbuf), str, NULL)) {
4782    if (0 != job->logthis) {
4783      msgs[0] = printqd_kw[86];
4784      msgs[1] = str;
4785      msgs[2] = NULL;
4786      log_multipart_message(NULL, 0UL, LOG_EMERG, 0, msgs, 2);
4787    }
4788    if (0 != dk4str8_cat_s(inbuf, sizeof(inbuf), printqd_kw[5], NULL)) {
4789      sz = dk4str8_len(inbuf);
4790      wr = sz;
4791      if ((NULL != job->soa) && (0 < job->szsoa)) {	$? ". UDP"
4792        (void)dk4socket_sendto(
4793	  job->sock, inbuf, &wr, 0, job->soa, job->szsoa, 0L, 0L, NULL
4794	);
4795      } else {						$? ". TCP or local"
4796        res = dk4socket_send(job->sock, inbuf, &wr, 0, 0L, 0L, NULL);
4797	switch (res) {
4798	  case DK4_SOCKET_RESULT_SUCCESS :
4799	  case DK4_SOCKET_RESULT_IN_PROGRESS : {
4800	    if (wr != sz) {				$? "! partial send"
4801	      res = DK4_SOCKET_RESULT_FAILED;
4802	    }
4803	  } break;
4804	}
4805	if (DK4_SOCKET_RESULT_FAILED == res) {	$? "! partial or no send"
4806	  close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
4807	}
4808      }
4809    } else {					$? "! text too long (2)"
4810      /* ERROR: Response text too long (BUG)! */
4811      log_1(NULL, 0UL, LOG_EMERG, 1, 116);
4812    }
4813  } else {					$? "! text too long (1)"
4814    /* ERROR: Response text too long (BUG)! */
4815    log_1(NULL, 0UL, LOG_EMERG, 1, 116);
4816  }
4817  $? "- send_text"
4818}
4819
4820
4821
4822/**	Send response for info or acct-check request.
4823	@param	job	Job structure.
4824	@param	account	Account structure containing limits and used pages.
4825	@param	summary	Summary: Allowed to print.
4826	@param	how	How to respond: 0=info, 1=acct-check/jobstart.
4827*/
4828static
4829void
4830send_response(
4831  pqd_rq_t	*job,
4832  pqd_account_t	*account,
4833  int		 summary,
4834  int		 how
4835)
4836{
4837  char		res[16+32*sizeof(dk4_um_t)];
4838  char		b1[16+8*sizeof(dk4_um_t)];
4839  int		ok	= 0;
4840  $? "+ send_response"
4841  switch (how) {
4842    case 0: {
4843      /* Limit Used Account Summary */
4844      if (DK4_UM_MAX == account->limit) {
4845        ok = dk4str8_cpy_s(res, sizeof(res), printqd_kw[84], NULL);
4846      } else {
4847        ok = dk4ma_write_c8_decimal_unsigned(
4848	  b1, sizeof(b1), account->limit, 0, NULL
4849	);
4850	if (0 != ok) {
4851	  ok = dk4str8_cpy_s(res, sizeof(res), b1, NULL);
4852	}
4853      }
4854      if (0 != ok) {
4855	ok = dk4ma_write_c8_decimal_unsigned(
4856	  b1, sizeof(b1), account->used, 0, NULL
4857	);
4858	if (0 != ok) {
4859	  ok = dk4str8_cat_s(res, sizeof(res), printqd_kw[85], NULL);
4860	  if (0 != ok) {
4861	    ok = dk4str8_cat_s(res, sizeof(res), b1, NULL);
4862	    if (0 != ok) {
4863	      ok = dk4ma_write_c8_decimal_unsigned(
4864	        b1, sizeof(b1), account->account, 0, NULL
4865	      );
4866	      if (0 != ok) {
4867	        ok = dk4str8_cat_s(res, sizeof(res), printqd_kw[85], NULL);
4868		if (0 != ok) {
4869		  ok = dk4str8_cat_s(res, sizeof(res), b1, NULL);
4870		  if (0 != ok) {
4871		    ok = dk4ma_write_c8_decimal_signed(
4872		      b1, sizeof(b1), (dk4_im_t)summary, 0, NULL
4873		    );
4874		    if (0 != ok) {
4875		      ok = dk4str8_cat_s(res,sizeof(res),printqd_kw[85],NULL);
4876		      if (0 != ok) {
4877		        ok = dk4str8_cat_s(res, sizeof(res), b1, NULL);
4878		      }
4879		    }
4880		  }
4881		}
4882	      }
4883	    }
4884	  }
4885	}
4886      }
4887      if (0 != ok) {
4888        send_text(job, res);
4889      } else {
4890        send_text(job, printqd_kw[83]);
4891      }
4892    } break;
4893    default: {
4894      if (0 != summary) {
4895        send_text(job, printqd_kw[80]);
4896      } else {
4897        switch (account->da) {
4898	  case 0: {
4899	    send_text(job, printqd_kw[81]);
4900	  } break;
4901	  default: {
4902	    send_text(job, printqd_kw[82]);
4903	  } break;
4904	}
4905      }
4906    } break;
4907  }
4908  $? "- send_response"
4909}
4910
4911
4912
4913/**	Get printer entry for name (printer or alias name).
4914	@param	name	Printer or alias name.
4915	@return	Printer entry.
4916*/
4917static
4918pqd_printer_t *
4919get_printer_for_name(const char *name)
4920{
4921  pqd_printer_t		*back	= NULL;
4922  pqd_printer_alias_t	*al	= NULL;
4923  $? "+ get_printer_for_name \"%s\"", TR_8STR(name)
4924  back = (pqd_printer_t *)dk4sto_it_find_like(conf.i_p, name, 1);
4925  if (NULL == back) {
4926    al = (pqd_printer_alias_t *)dk4sto_it_find_like(conf.i_a, name, 1);
4927    if (NULL != al) {
4928      back = al->pr;
4929    }
4930  }
4931  $? "- get_printer_for_name %s", TR_8PTR(back)
4932  return back;
4933}
4934
4935
4936
4937/**	Save one dk4_um_t value to database.
4938	@param	kwi	Keyword index for key start.
4939	@param	un	User name.
4940	@param	cn	Class name.
4941	@param 	val	Value to store.
4942	@return	1 on success, 0 on error.
4943*/
4944static
4945int
4946save_one_number(
4947  size_t	 kwi,
4948  const char	*un,
4949  const char	*cn,
4950  dk4_um_t	 val
4951)
4952{
4953  int		 back	= 0;
4954  int		 strok	= 0;
4955  $? "+ save_one_number \"%s\" \"%s\" %lu", TR_8STR(un), TR_8STR(cn), (unsigned long)val
4956  if (0 != dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[kwi], NULL)) {
4957    if (0 != dk4str8_cat_s(kb, sizeof(kb), cn, NULL)) {
4958      if (0 != dk4str8_cat_s(kb, sizeof(kb), printqd_kw[8], NULL)) {
4959        if (0 != dk4str8_cat_s(kb, sizeof(kb), un, NULL)) {
4960	  if ((dk4_um_t)0UL != val) {
4961	    if(0 != dk4ma_write_c8_decimal_unsigned(vb,sizeof(vb),val,0,NULL)) {
4962	      strok = 1;
4963	      $? ". db set \"%s\"=\"%s\"", kb, vb
4964	      if (0 != dbi_set(db, kb, vb, NULL)) {	$? ". saved"
4965	        back = 1;
4966	      }
4967	      else {						$? "! db set"
4968	        /* ERROR: Failed to set DB entry */
4969		log_1(NULL, 0UL, LOG_EMERG, 1, 117);
4970	      }
4971	    }
4972#if TRACE_DEBUG
4973	    else {						$? "! to text"
4974	    }
4975#endif
4976	  } else {
4977	    strok = 1;
4978	    $? ". db del \"%s\"", kb
4979	    if (0 != dbi_del(db, kb, NULL)) {		$? ". deleted"
4980	      back = 1;
4981	    } else {						$? "! db del"
4982	      /* ERROR: Failed to delete DB entry */
4983	      log_1(NULL, 0UL, LOG_EMERG, 1, 118);
4984	    }
4985	  }
4986	}
4987      }
4988    }
4989  }
4990  if (0 == strok) {
4991    /* ##### ERROR: Strings too long */
4992  }
4993  $? "- save_one_number %d", back
4994  return back;
4995}
4996
4997
4998
4999/**	Save changes in account to database.
5000	@param	oldacc	Previous account information, may be NULL.
5001	@param	newacc	Updated account information, not NULL.
5002	@param	un	User name.
5003	@param	cl	Class structure.
5004	@return	1 on success, 0 on error.
5005*/
5006static
5007int
5008save_account_for_user_and_class(
5009  pqd_account_t	*oldacc,
5010  pqd_account_t	*newacc,
5011  const char	*un,
5012  pqd_class_t	*cl
5013)
5014{
5015  int		 mustsave;		/* Flag: Must save item */
5016  int		 back		= 1;
5017  $? "+ save_account_for_user_and_class"
5018  mustsave = 1;
5019  if (NULL != oldacc) {
5020    if (oldacc->used == newacc->used) {
5021      mustsave = 0;
5022    }
5023  }
5024  if (0 != mustsave) {		$? ". must save pages"
5025    if (0 == save_one_number(88, un, cl->name, newacc->used)) {
5026      back = 0;
5027    }
5028  }
5029  mustsave = 1;
5030  if (NULL != oldacc) {
5031    if (oldacc->account == newacc->account) {
5032      mustsave = 0;
5033    }
5034  }
5035  if (0 != mustsave) {		$? ". must save account"
5036    if (0 == save_one_number(89, un, cl->name, newacc->account)) {
5037      back = 0;
5038    }
5039  }
5040  $? "- save_account_for_user_and_class %d", back
5041  return back;
5042}
5043
5044
5045
5046/**	Retrieve account data for a user in a specified printer class.
5047	@param	account	Data structure to fill.
5048	@param	un	User name.
5049	@param	cl	Printer class.
5050	@param	mi	Flag: Must initialize account before filling it.
5051	@return	1 on success, 0 on error.
5052*/
5053static
5054int
5055retrieve_account_for_user_and_class(
5056  pqd_account_t	*account,
5057  const char	*un,
5058  pqd_class_t	*cl,
5059  int		 mi
5060)
5061{
5062  char		**grmem	= NULL;
5063  const char	 *ep	= NULL;			/* End of data */
5064  pqd_limit_t	 *lim	= NULL;			/* Limit set */
5065  struct passwd	 *pw	= NULL;			/* Password struct */
5066  struct group	 *gr	= NULL;			/* Group struct */
5067  dk4_um_t	  um	= (dk4_um_t)0UL;	/* Value found in text */
5068  gid_t		  pg	= 0;			/* Primary group */
5069  int		  ingr	= 0;			/* Flag: User in group */
5070  int		 back	= 0;
5071  $? "+ retrieve_account_for_user_and_class \"%s\" %s", TR_8STR(un), TR_PTR(cl)
5072  /*
5073	Check arguments
5074  */
5075  if (NULL == account) {
5076    goto finished;
5077  }
5078  if (0 != mi) {
5079    account_init(account);
5080  }
5081  if ((NULL == un) || (NULL == cl)) {
5082    goto finished;
5083  }
5084  if (NULL == cl->name) {
5085    goto finished;
5086  }
5087  /*
5088	Use default action from class
5089  */
5090  account->da = cl->da;
5091  /*
5092	Find number of pages already printed
5093  */
5094  if (0 != dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[88], NULL)) {
5095    if (0 != dk4str8_cat_s(kb, sizeof(kb), cl->name, NULL)) {
5096      if (0 != dk4str8_cat_s(kb, sizeof(kb), printqd_kw[8], NULL)) {
5097        if (0 != dk4str8_cat_s(kb, sizeof(kb), un, NULL)) {
5098	  $? ". db get \"%s\"", kb
5099	  if (0 != dbi_get(db, kb, vb, sizeof(vb), NULL)) {
5100	    if (0 != dk4ma_input_c8_dec_dk4_um_t(&um, vb, &ep, 1, NULL)) {
5101	      account->used = um;
5102	    }
5103#if TRACE_DEBUG
5104	    else {	$? "! not a number"
5105	    }
5106#endif
5107	  }
5108#if TRACE_DEBUG
5109	  else {	$? "! db get"
5110	  }
5111#endif
5112	}
5113      }
5114    }
5115  }
5116  /*
5117	Find number of pages in personal print account
5118  */
5119  if (0 != dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[89], NULL)) {
5120    if (0 != dk4str8_cat_s(kb, sizeof(kb), cl->name, NULL)) {
5121      if (0 != dk4str8_cat_s(kb, sizeof(kb), printqd_kw[8], NULL)) {
5122        if (0 != dk4str8_cat_s(kb, sizeof(kb), un, NULL)) {
5123	  $? ". db get \"%s\"", kb
5124	  if (0 != dbi_get(db, kb, vb, sizeof(vb), NULL)) {
5125	    um = (dk4_um_t)0UL; ep = NULL;
5126	    if (0 != dk4ma_input_c8_dec_dk4_um_t(&um, vb, &ep, 1, NULL)) {
5127	      account->account = um;
5128	    }
5129#if TRACE_DEBUG
5130	    else {	$? "! not a number"
5131	    }
5132#endif
5133	  }
5134#if TRACE_DEBUG
5135	  else {	$? "! db get"
5136	  }
5137#endif
5138	}
5139      }
5140    }
5141  }
5142  /*
5143	Find user limit
5144  */
5145  $? ". check user limit"
5146  lim = (pqd_limit_t *)dk4sto_it_find_like(cl->i_u, un, 1);
5147  if (NULL != lim) {			$? ". user limit found"
5148    account->limit = lim->limit;
5149    back = 1;
5150    goto finished;
5151  }
5152  /*
5153	Find group limit
5154  */
5155  $? ". check group limits"
5156  pw = getpwnam(un);
5157  if (NULL != pw) {				$? ". pw found"
5158    pg = pw->pw_gid;
5159    dk4sto_it_reset(cl->i_g);
5160    do {
5161      $? ". loop start"
5162      lim = (pqd_limit_t *)dk4sto_it_next(cl->i_g);
5163      if (NULL != lim) {			$? ". yet another group limit"
5164        if (lim->limit > account->limit) {	$? ". group limit is higher"
5165	  $? ". group=\"%s\" limit=%lu", TR_8STR(lim->name), (unsigned long)(lim->limit)
5166	  gr = getgrnam(lim->name);
5167	  if (NULL != gr) {			$? ". gr found"
5168	    if (pg == gr->gr_gid) {		$? ". is users primary group"
5169	      account->limit = lim->limit;
5170	      back = 1;
5171	    } else {				$? ". not primary group"
5172	      if (NULL != gr->gr_mem) {
5173	        grmem = gr->gr_mem;
5174		ingr  = 0;
5175		while ((0 == ingr) && (NULL != *grmem)) {
5176		  $? ". compare \"%s\" \"%s\"", TR_8STR(un), TR_8STR(*grmem)
5177		  if (0 == strcmp(un, *grmem)) {
5178		    ingr = 1;
5179		  }
5180		  grmem++;
5181		}
5182		if (0 != ingr) {		$? ". user is in group"
5183		  account->limit = lim->limit;
5184		  back = 1;
5185		}
5186#if TRACE_DEBUG
5187		else {				$? ". user is not in group"
5188		}
5189#endif
5190	      }
5191#if TRACE_DEBUG
5192	      else {				$? ". no members in group"
5193	      }
5194#endif
5195	    }
5196	  }
5197#if TRACE_DEBUG
5198	  else {				$? "! group not found"
5199	  }
5200#endif
5201	}
5202      }
5203#if TRACE_DEBUG
5204      else {				$? "! no more group limits"
5205      }
5206#endif
5207      $? ". loop end"
5208    } while(NULL != lim);
5209    if (0 != back) {
5210      goto finished;
5211    }
5212  }
5213#if TRACE_DEBUG
5214  else {				$? "! getpwnam"
5215  }
5216#endif
5217  /*
5218	Apply default limit
5219  */
5220  $? ". apply default limit"
5221  account->limit = cl->dl;
5222  back = 1;
5223
5224  finished:
5225  $? "- retrieve_account_for_user_and_class %d", back
5226  return back;
5227}
5228
5229
5230
5231/**	Retrieve user account data for a given user name and a given printer
5232	name.
5233	@param	account		Account structure to fill.
5234	@param	printer_name	Print queue name or alias.
5235	@param	user_name	User name.
5236	@return	1 on success, 0 on error.
5237*/
5238static
5239int
5240retrieve_account_data(
5241  pqd_account_t	*account,
5242  const char	*printer_name,
5243  const char	*user_name
5244)
5245{
5246  pqd_printer_t	 *pr	= NULL;			/* Printer */
5247  pqd_class_t	 *cl	= NULL;			/* Class */
5248  int		  back	= 0;
5249  $? "+ retrieve_account_data   p=\"%s\" u=\"%s\"", TR_8STR(printer_name), TR_8STR(user_name)
5250  /*
5251	Initialize account data
5252  */
5253  account_init(account);
5254  /*
5255	Find printer
5256  */
5257  pr = get_printer_for_name(printer_name);
5258  if (NULL == pr) {			$? "! no such printer"
5259    goto finished;
5260  }
5261  /*
5262	Find class for printer
5263  */
5264  cl = pr->cl;
5265  if (NULL == cl) {			$? "! no such class"
5266    goto finished;
5267  }
5268  if (NULL == cl->name) {		$? "! no class name"
5269    goto finished;
5270  }
5271  /*
5272	Search for data
5273  */
5274  back = retrieve_account_for_user_and_class(account, user_name, cl, 0);
5275  /*
5276	Return result
5277  */
5278  finished:
5279  $? "- retrieve_account_data %d", back
5280  return back;
5281}
5282
5283
5284
5285/**	Add a number of pages for a print job to existing account data.
5286	@param	account	Account data for user.
5287	@param	pages	Number of pages used by print job.
5288*/
5289static
5290void
5291account_add_pages_from_job(pqd_account_t *account, dk4_um_t pages)
5292{
5293  dk4_um_t		available;
5294
5295  /*	The limit is used first.
5296  */
5297  if (account->limit > account->used) {
5298    available = account->limit - account->used;
5299    if (available >= pages) {
5300      account->used += pages;
5301      pages = (dk4_um_t)0UL;
5302    } else {
5303      account->used = account->limit;
5304      pages -= available;
5305    }
5306  }
5307  /*	The personal print account is used only if the limit is reached.
5308  */
5309  if ((dk4_um_t)0UL < pages) {
5310    if (account->account >= pages) {
5311      account->account -= pages;
5312      pages = (dk4_um_t)0UL;
5313    } else {
5314      pages -= account->account;
5315      account->account = (dk4_um_t)0UL;
5316    }
5317  }
5318  /*	Finally keep track of pages printed over limit.
5319  */
5320  if ((dk4_um_t)0UL < pages) {
5321    if ((DK4_UM_MAX - pages) >= account->used) {
5322      account->used += pages;
5323    } else {
5324      account->used =  DK4_UM_MAX;
5325    }
5326  }
5327}
5328
5329
5330
5331/**	Find summary (allowed to print) for account details.
5332	@param	limit	Limit (maximum number of pages) for user.
5333	@param	used	Number of used pages within the limit.
5334	@param	account	Number of pages in additional personal print account.
5335	@return	1 if printing is allowed, 0 otherwise.
5336*/
5337static
5338int
5339account_summary(dk4_um_t limit, dk4_um_t used, dk4_um_t account)
5340{
5341  int		 back	= 0;
5342  $? "+ account_summary l=%lu u=%lu a=%lu", (unsigned long)limit, (unsigned long)used, (unsigned long)account
5343  if (DK4_UM_MAX == limit) {		$? ". unlimited"
5344    back = 1;
5345  } else {
5346    if (used < limit) {			$? ". limit not yet reached"
5347      back = 1;
5348    } else {
5349      if ((dk4_um_t)0UL < account) {	$? ". positive account value"
5350        back = 1;
5351      }
5352    }
5353  }
5354  $? "- account_summary %d", back
5355  return back;
5356}
5357
5358
5359/**	Process info or acct-check request, only retrieve data
5360	without modifying anything.
5361	@param	job	Data for one request to process.
5362	@param	how	Response type: 0=info, 1=acct-check.
5363*/
5364static
5365void
5366rq_retrieve_data(pqd_rq_t *job, int how)
5367{
5368  pqd_account_t	 account;
5369  char		*printer_name	= NULL;
5370  char		*user_name	= NULL;
5371  int		 summary	= 0;
5372  $? "+ rq_retrieve_data %d", how
5373  printer_name = job->argptr;
5374  if (NULL != printer_name) {
5375    user_name = dk4str8_next(printer_name, NULL);
5376    if (NULL != user_name) {
5377      (void)dk4str8_next(user_name, NULL);
5378    }
5379  }
5380  if ((NULL != printer_name) && (NULL != user_name)) {
5381    if (0 != retrieve_account_data(&account, printer_name, user_name)) {
5382#if VERSION_BEFORE_20160329
5383      if ((account.used < account.limit) || ((dk4_um_t)0UL < account.account)) {
5384        summary = 1;
5385      }
5386#else
5387      summary = account_summary(account.limit, account.used, account.account);
5388#endif
5389    }
5390    send_response(job, &account, summary, how);
5391  } else {
5392    /*	Close connection on malformed request.
5393    */
5394    if ((NULL == job->soa) && (0 == job->szsoa)) {
5395      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5396    }
5397  }
5398  $? "- rq_retrieve_data"
5399}
5400
5401
5402
5403/**	Process info request.
5404	@param	job	Data for one request to process.
5405*/
5406static
5407void
5408rq_info(pqd_rq_t *job)
5409{
5410  $? "+ rq_info"
5411  rq_retrieve_data(job, 0);
5412  $? "- rq_info"
5413}
5414
5415
5416
5417/**	Find final quote for current word.
5418	@param	str	String to find quote in.
5419	@param	quote	Quote character.
5420	@return	Pointer to found quote on success, NULL on error.
5421*/
5422static
5423char *
5424lprng_final_quote(char *str, char quote)
5425{
5426  char		*back	= NULL;
5427  $? "+ lprng_final_quote %c %s", quote, TR_8STR(str)
5428  while (('\0' != *str) && (NULL == back)) {
5429    if (quote == *str) {
5430      *str = '\0';
5431      back = str;
5432      back++;
5433      back = dk4str8_start(back, NULL);
5434    } else {
5435      if ('\\' == *str) {
5436        if ('\0' != str[1]) {
5437	  dk4str8_cpy_to_left(str, &(str[1]));
5438	  str++;
5439	} else {
5440	  *str = '\0';
5441	}
5442      } else {
5443        str++;
5444      }
5445    }
5446  }
5447  $? "- lprng_final_quote \"%s\"", TR_8STR(back)
5448  return back;
5449}
5450
5451
5452
5453/**	Find start of next text word in LPRng accounting line.
5454	@param	str	Current word (remaining text) in accounting line.
5455	@return	Pointer to next word if found, NULL on error.
5456*/
5457static
5458char *
5459lprng_next_word(char *str)
5460{
5461  char		*back	= NULL;
5462  $? "+ lprng_next_word \"%s\"", TR_8STR(str)
5463  switch (*str) {
5464    case '\'' : {
5465      back = lprng_final_quote(&(str[1]), '\'');
5466    } break;
5467    case '"'  : {
5468      back = lprng_final_quote(&(str[1]), '"');
5469    } break;
5470    default : {
5471      back = dk4str8_next(str, NULL);
5472    } break;
5473  } $? "- lprng_next_word \"%s\"", TR_8STR(back)
5474  return back;
5475}
5476
5477
5478
5479/**	Fill data structure from LPRng accounting line.
5480	@param	liptr		Structure to fill.
5481	@param	textline	The text line to use.
5482	@return	1 on success, 0 on error.
5483*/
5484static
5485int
5486lprng_info_from_line(lprng_info_t *liptr, char *textline)
5487{
5488  char		*next;
5489  int		 back	= 1;
5490  $? "+ lprng_info_from_line \"%s\"", TR_8STR(textline)
5491  DK4_MEMRES(liptr, sizeof(lprng_info_t));
5492  liptr->username = NULL; liptr->queuename = NULL;
5493  liptr->jobtitle = NULL; liptr->pagecount = NULL;
5494  while (NULL != textline) {
5495    next = lprng_next_word(textline);
5496    if (('\'' == *textline) || ('"' == *textline)) {
5497      textline++;
5498    }
5499    if ('-' == *textline) {
5500      textline++;
5501      switch (*textline) {
5502        case 'n': {
5503	  liptr->username = ++textline;
5504	} break;
5505	case 'P': {
5506	  liptr->queuename = ++textline;
5507	} break;
5508	case 'J': {
5509	  liptr->jobtitle = ++textline;
5510	} break;
5511	case 'p': {
5512	  liptr->pagecount = ++textline;
5513	} break;
5514      }
5515    } else {		$? "! not a minus"
5516#if 0
5517      /*	2016-02-15	Simply ignore non-option text.
5518      */
5519      back = 0;
5520      /* ERROR: No minus sign */
5521#endif
5522    }
5523    textline = next;
5524  }
5525  $? "- lprng_info_from_line %d", back
5526  return back;
5527}
5528
5529
5530
5531/**	Process jobstart request.
5532	@param	job	Data for one request to process.
5533*/
5534static
5535void
5536rq_jobstart(pqd_rq_t *job)
5537{
5538  lprng_info_t		 lpi;
5539  pqd_account_t		 account;
5540  char			*un	= NULL;
5541  char			*qn	= NULL;
5542  int			 summ	= 0;
5543  $? "+ rq_jobstart"
5544  if (0 != lprng_info_from_line(&lpi, job->argptr)) {
5545    un = lpi.username; qn = lpi.queuename;
5546  }
5547  if ((NULL != un) && (NULL != qn)) {
5548    if (0 != retrieve_account_data(&account, qn, un)) {
5549      summ = account_summary(account.limit, account.used, account.account);
5550    }
5551    send_response(job, &account, summ, 1);
5552  } else {
5553    /*	Close connection on malformed request.
5554    */
5555    if ((NULL == job->soa) && (0 == job->szsoa)) {
5556      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5557    }
5558  }
5559  $? "- rq_jobstart"
5560}
5561
5562
5563
5564/**	Report change result to log file.
5565	@param	un	User name.
5566	@param	cl	Class.
5567	@param	olda	Old account values.
5568	@param	newa	New account values.
5569	@param	pages	Number of pages to add.
5570	@param	doadd	Flag: Show added pages instead of used pages.
5571*/
5572static
5573void
5574log_old_and_new_account(
5575  const char		*un,
5576  const pqd_class_t	*cl,
5577  const pqd_account_t	*olda,
5578  const pqd_account_t	*newa,
5579  dk4_um_t		 pages,
5580  int			 doadd
5581)
5582{
5583  char		 nb[8*sizeof(dk4_um_t)];
5584  const char	*msgs[2];
5585  int		 res	= 1;
5586  if (0 == dk4str8_cpy_s(kb, sizeof(kb), un, NULL)) { res = 0; }
5587  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[85], NULL)) { res = 0; }
5588  if (0 == dk4str8_cat_s(kb, sizeof(kb), cl->name, NULL)) { res = 0; }
5589  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[7], NULL)) { res = 0; }
5590  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[145], NULL)) { res = 0; }
5591  if (DK4_UM_MAX == olda->limit) {
5592    if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[151], NULL)) { res = 0; }
5593  } else {
5594    if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),olda->limit,0,NULL)) {
5595      if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5596    } else {
5597      res = 0;
5598    }
5599  }
5600  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[146], NULL)) { res = 0; }
5601  if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),olda->used,0,NULL)) {
5602    if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5603  } else {
5604    res = 0;
5605  }
5606  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[147], NULL)) { res = 0; }
5607  if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),olda->account,0,NULL)) {
5608    if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5609  } else {
5610    res = 0;
5611  }
5612  switch (doadd) {
5613    case 1: {
5614      if (0 == dk4str8_cat_s(kb,sizeof(kb),printqd_kw[153],NULL)) { res = 0; }
5615    } break;
5616    default: {
5617      if (0 == dk4str8_cat_s(kb,sizeof(kb),printqd_kw[148],NULL)) { res = 0; }
5618    } break;
5619  }
5620  if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),pages,0,NULL)) {
5621    if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5622  } else {
5623    res = 0;
5624  }
5625  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[149], NULL)) { res = 0; }
5626  if (DK4_UM_MAX == newa->limit) {
5627    if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[151], NULL)) { res = 0; }
5628  } else {
5629    if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),newa->limit,0,NULL)) {
5630      if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5631    } else {
5632      res = 0;
5633    }
5634  }
5635  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[146], NULL)) { res = 0; }
5636  if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),newa->used,0,NULL)) {
5637    if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5638  } else {
5639    res = 0;
5640  }
5641  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[147], NULL)) { res = 0; }
5642  if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),newa->account,0,NULL)) {
5643    if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; }
5644  } else {
5645    res = 0;
5646  }
5647  if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[150], NULL)) { res = 0; }
5648  if (0 != res) {
5649    msgs[0] = kb;
5650    msgs[1] = NULL;
5651    log_multipart_message(NULL, 0UL, LOG_EMERG, 0, msgs, 1);
5652  }
5653}
5654
5655
5656
5657/**	Charge user for a print job.
5658	@param	un	User name.
5659	@param	cl	Printer class of printer the job was printed on.
5660	@param	pcval	Number of pages in print job.
5661*/
5662static
5663void
5664do_charge(
5665  const char	*un,
5666  pqd_class_t	*cl,
5667  dk4_um_t	 pcval
5668)
5669{
5670  pqd_account_t		 oldacc;
5671  pqd_account_t		 newacc;
5672  $? "+ do_charge \"%s\" \"%s\" %lu", un, TR_8STR(cl->name), (unsigned long)pcval
5673  if (0 != retrieve_account_for_user_and_class(&oldacc, un, cl, 1)) {
5674    DK4_MEMCPY(&newacc, &oldacc, sizeof(pqd_account_t));
5675    account_add_pages_from_job(&newacc, pcval);
5676    log_old_and_new_account(un, cl, &oldacc, &newacc, pcval, 0);
5677    if (0 == save_account_for_user_and_class(&oldacc, &newacc, un, cl)) {
5678      $? "! failed to save updated account data"
5679      /* ERROR: Failed to update account data */
5680      log_5(NULL, 0UL, LOG_EMERG, 1, 156, 157, 158, un, cl->name);
5681    }
5682  }
5683#if TRACE_DEBUG
5684  else {		$? "! failed to retrieve existing account data"
5685  }
5686#endif
5687  $? "- do_charge"
5688}
5689
5690
5691
5692/**	On start of file printing save user name, job name and pagecount
5693	value to database for later use.
5694	@param	job	Job structure.
5695	@param	un	User name.
5696	@param	pn	Printer name.
5697	@param	jn	Job name.
5698	@param	pc	Printer page count at start of job.
5699*/
5700static
5701void
5702do_filestart(
5703  pqd_rq_t	*job,
5704  const char	*un,
5705  const char	*pn,
5706  const char	*jn,
5707  const char	*pc
5708)
5709{
5710  const char	*ep	= NULL;
5711  pqd_printer_t	*pr	= NULL;
5712  pqd_class_t	*cl	= NULL;
5713  dk4_um_t	 pcstt	= (dk4_um_t)0UL;
5714  int		 res	= 0;
5715  $? "+ do_filestart %s \"%s\" \"%s\" \"%s\" \"%s\"", TR_8PTR(job),un,pn,jn,pc
5716  pr = get_printer_for_name(pn);
5717  if (NULL != pr) {
5718    cl = pr->cl;
5719  }
5720  if (NULL != cl) {
5721    res = dk4ma_input_c8_dec_dk4_um_t(&pcstt, pc, &ep, 1, NULL);
5722  }
5723  if ((NULL != pr) && (NULL != cl) && (0 != res)) {
5724    $? ". pr=\"%s\" cl=\"%s\"", pr->name, TR_8STR(cl->name)
5725    if (0 == dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[90], NULL)) { res = 0; }
5726    if (0 == dk4str8_cat_s(kb, sizeof(kb), pr->name, NULL)) { res = 0; }
5727    if (0 == dk4str8_cpy_s(vb, sizeof(vb), pc, NULL)) { res = 0; }
5728    if (0 == dk4str8_cat_s(vb, sizeof(vb), printqd_kw[8], NULL)) { res = 0; }
5729    if (0 == dk4str8_cat_s(vb, sizeof(vb), un, NULL)) { res = 0; }
5730    if (0 == dk4str8_cat_s(vb, sizeof(vb), printqd_kw[8], NULL)) { res = 0; }
5731    if (0 == dk4str8_cat_s(vb, sizeof(vb), jn, NULL)) { res = 0; }
5732    if (0 != res) {	$? ". db set \"%s\"=\"%s\"", kb, vb
5733      if (0 == dbi_set(db, kb, vb, NULL)) {	$? "! db set"
5734        /* ERROR: Failed to set database entry */
5735	log_1(NULL, 0UL, LOG_EMERG, 1, 117);
5736      }
5737    }
5738#if TRACE_DEBUG
5739    else {		$? "! key or value name too long"
5740    }
5741#endif
5742  } else {		$? "! malformed request"
5743    /*	Close connection on malformed request
5744    */
5745    if ((NULL == job->soa) && (0 == job->szsoa)) {
5746      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5747    }
5748  }
5749  $? "- do_filestart"
5750}
5751
5752
5753
5754/**	On end of file calculate page number used by the job and
5755	save modified account to database.
5756	@param	job	Job structure.
5757	@param	un	User name.
5758	@param	pn	Printer name.
5759	@param	jn	Job name.
5760	@param	pc	Printer page count at start of job.
5761*/
5762static
5763void
5764do_fileend(
5765  pqd_rq_t	*job,
5766  const char	*un,
5767  const char	*pn,
5768  const char	*jn,
5769  const char	*pc
5770)
5771{
5772  const char	*ep	= NULL;			/* End of processed text */
5773  pqd_printer_t	*pr	= NULL;			/* Printer */
5774  pqd_class_t	*cl	= NULL;			/* Printer class */
5775  char		*oun	= NULL;			/* Old user name */
5776  char		*opc	= NULL;			/* Old page counter value */
5777  char		*ojn	= NULL;			/* Old job name */
5778  dk4_um_t	 pcstt	= (dk4_um_t)0UL;	/* Page counter at job start */
5779  dk4_um_t	 pcend	= (dk4_um_t)0UL;	/* Page counter at job end */
5780  int		 res	= 0;
5781
5782  $? "+ do_fileend %s \"%s\" \"%s\" \"%s\" \"%s\"", TR_8PTR(job), un, pn, jn, pc
5783  pr = get_printer_for_name(pn);
5784  if (NULL != pr) {
5785    cl = pr->cl;
5786  }
5787  if (NULL != cl) {
5788    res = dk4ma_input_c8_dec_dk4_um_t(&pcend, pc, &ep, 1, NULL);
5789  }
5790  if ((NULL != pr) && (NULL != cl) && (0 != res)) {
5791    if (0 == dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[90], NULL)) { res = 0; }
5792    if (0 == dk4str8_cat_s(kb, sizeof(kb), pr->name, NULL)) { res = 0; }
5793    if (0 != res) {	$? ". db get \"%s\"", kb
5794      if (0 != dbi_get(db, kb, vb, sizeof(vb), NULL)) {
5795        $? ". db del \"%s\"", kb
5796        if (0 == dbi_del(db, kb, NULL)) {
5797          /* ERROR: Failed to delete db entry */
5798	  log_1(NULL, 0UL, LOG_EMERG, 1, 118);
5799        }
5800        $? ". db res \"%s\"", vb
5801        opc = dk4str8_start(vb, NULL);
5802	if (NULL != opc) {
5803	  oun = dk4str8_chr(opc, ':');
5804	  if (NULL != oun) {
5805	    *(oun++) = '\0';
5806	    oun = dk4str8_start(oun, NULL);
5807	  }
5808	}
5809	if (NULL != oun) {
5810	  ojn = dk4str8_chr(oun, ':');
5811	  if (NULL != ojn) {
5812	    *(ojn++) = '\0';
5813	    ojn = dk4str8_start(ojn, NULL);
5814	  }
5815	}
5816	if ((NULL != opc) && (NULL != oun) && (NULL != ojn)) {
5817	  $? ". user       \"%s\", old \"%s\"", un, oun
5818	  $? ". job        \"%s\", old \"%s\"", jn, ojn
5819	  $? ". page count \"%s\", old \"%s\"", pc, opc
5820	  if (0 == strcmp(ojn, jn)) {
5821	    if (0 == strcmp(oun, un)) {
5822	      ep = NULL;
5823	      res = dk4ma_input_c8_dec_dk4_um_t(&pcstt, opc, &ep, 1, NULL);
5824	      if (0 != res) {
5825	        if (pcend > pcstt) {	$? ". charge user"
5826		  do_charge(un, cl, (pcend - pcstt));
5827		}
5828#if TRACE_DEBUG
5829		else {			$? "! pagecount unchanged"
5830		}
5831#endif
5832	      }
5833#if TRACE_DEBUG
5834	      else {			$? "! old pagecount not numeric"
5835	      }
5836#endif
5837	    }
5838#if TRACE_DEBUG
5839	    else {			$? "! user name mismatch"
5840	    }
5841#endif
5842	  }
5843#if TRACE_DEBUG
5844	  else {			$? "! job name mismatch"
5845	  }
5846#endif
5847	}
5848#if TRACE_DEBUG
5849	else {				$? "! incomplete entry"
5850	}
5851#endif
5852      }
5853      else {		$? "! not found"
5854        $? ". db del \"%s\"", kb
5855        if (0 == dbi_del(db, kb, NULL)) {
5856          /* ERROR: Failed to delete db entry */
5857	  log_1(NULL, 0UL, LOG_EMERG, 1, 118);
5858        }
5859      }
5860    }
5861#if TRACE_DEBUG
5862    else {		$? "! printer name too long"
5863    }
5864#endif
5865  } else {		$? "! malformed request"
5866    /*	Close connection on malformed request
5867    */
5868    if ((NULL == job->soa) && (0 == job->szsoa)) {
5869      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5870    }
5871  }
5872  $? "- do_fileend"
5873}
5874
5875
5876
5877/**	Process filestart request.
5878	@param	job	Data for one request to process.
5879*/
5880static
5881void
5882rq_filestart(pqd_rq_t *job)
5883{
5884  lprng_info_t		 lpi;
5885  char			*un	= NULL;
5886  char			*pn	= NULL;
5887  char			*jn	= NULL;
5888  char			*pc	= NULL;
5889  $? "+ rq_filestart"
5890  if (0 != lprng_info_from_line(&lpi, job->argptr)) {
5891    un = lpi.username; pn = lpi.queuename;
5892    jn = lpi.jobtitle; pc = lpi.pagecount;
5893  }
5894  if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) {
5895    do_filestart(job, un, pn, jn, pc);
5896  } else {
5897    /*	Close connection on malformed request
5898    */
5899    if ((NULL == job->soa) && (0 == job->szsoa)) {
5900      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5901    }
5902  }
5903  $? "- rq_filestart"
5904}
5905
5906
5907
5908/**	Process fileend request.
5909	@param	job	Data for one request to process.
5910*/
5911static
5912void
5913rq_fileend(pqd_rq_t *job)
5914{
5915  lprng_info_t		 lpi;
5916  char			*un	= NULL;
5917  char			*pn	= NULL;
5918  char			*jn	= NULL;
5919  char			*pc	= NULL;
5920  $? "+ rq_fileend"
5921  if (0 != lprng_info_from_line(&lpi, job->argptr)) {
5922    un = lpi.username; pn = lpi.queuename;
5923    jn = lpi.jobtitle; pc = lpi.pagecount;
5924  }
5925  if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) {
5926    do_fileend(job, un, pn, jn, pc);
5927  } else {
5928    /*	Close connection on malformed request
5929    */
5930    if ((NULL == job->soa) && (0 == job->szsoa)) {
5931      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5932    }
5933  }
5934  $? "- rq_fileend"
5935}
5936
5937
5938
5939/**	Process acct-check request.
5940	@param	job	Data for one request to process.
5941*/
5942static
5943void
5944rq_acct_check(pqd_rq_t *job)
5945{
5946  $? "+ rq_acct_check"
5947  rq_retrieve_data(job, 1);
5948  $? "- rq_acct_check"
5949}
5950
5951
5952
5953/**	Process acct-start request.
5954	@param	job	Data for one request to process.
5955*/
5956static
5957void
5958rq_acct_start(pqd_rq_t *job)
5959{
5960  char		*un	= NULL;
5961  char		*pn	= NULL;
5962  char		*jn	= NULL;
5963  char		*pc	= NULL;
5964  $? "+ rq_acct_start"
5965  pn = job->argptr;
5966  if (NULL != pn) {
5967    un = dk4str8_next(pn, NULL);
5968  }
5969  if (NULL != un) {
5970    pc = dk4str8_next(un, NULL);
5971  }
5972  if (NULL != pc) {
5973    jn = dk4str8_next(pc, NULL);
5974  }
5975  if (NULL != jn) {
5976    (void)dk4str8_next(jn, NULL);
5977  }
5978  if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) {
5979    do_filestart(job, un, pn, jn, pc);
5980  } else {
5981    /*	Close connection on malformed request
5982    */
5983    if ((NULL == job->soa) && (0 == job->szsoa)) {
5984      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
5985    }
5986  }
5987  $? "- rq_acct_start"
5988}
5989
5990
5991
5992/**	Process acct-end request.
5993	@param	job	Data for one request to process.
5994*/
5995static
5996void
5997rq_acct_end(pqd_rq_t *job)
5998{
5999  char		*un	= NULL;
6000  char		*pn	= NULL;
6001  char		*jn	= NULL;
6002  char		*pc	= NULL;
6003  $? "+ rq_acct_end"
6004  pn = job->argptr;
6005  if (NULL != pn) {
6006    un = dk4str8_next(pn, NULL);
6007  }
6008  if (NULL != un) {
6009    pc = dk4str8_next(un, NULL);
6010  }
6011  if (NULL != pc) {
6012    jn = dk4str8_next(pc, NULL);
6013  }
6014  if (NULL != jn) {
6015    (void)dk4str8_next(jn, NULL);
6016  }
6017  if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) {
6018    do_fileend(job, un, pn, jn, pc);
6019  } else {
6020    /*	Close connection on malformed request
6021    */
6022    if ((NULL == job->soa) && (0 == job->szsoa)) {
6023      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
6024    }
6025  }
6026  $? "- rq_acct_end"
6027}
6028
6029
6030
6031/**	Process acct-charge request.
6032	@param	job	Data for one request to process.
6033*/
6034static
6035void
6036rq_acct_charge(pqd_rq_t *job)
6037{
6038  const char	*ep	= NULL;
6039  pqd_printer_t	*pr	= NULL;
6040  pqd_class_t	*cl	= NULL;
6041  char		*un	= NULL;
6042  char		*pn	= NULL;
6043  char		*pc	= NULL;
6044  dk4_um_t	 pcval	= (dk4_um_t)0UL;
6045  $? "+ rq_acct_charge"
6046  pn = job->argptr;
6047  if (NULL != pn) {
6048    un = dk4str8_next(pn, NULL);
6049  }
6050  if (NULL != un) {
6051    pc = dk4str8_next(un, NULL);
6052  }
6053  if (NULL != pc) {
6054    (void)dk4str8_next(pc, NULL);
6055  }
6056  if (NULL != pn) {
6057    pr = get_printer_for_name(pn);
6058  }
6059  if (NULL != pr) {
6060    cl = pr->cl;
6061  }
6062  if (
6063    (NULL != un) && (NULL != pn) && (NULL != pc) && (NULL != pr) && (NULL != cl)
6064  )
6065  {
6066    if (0 != dk4ma_input_c8_dec_dk4_um_t(&pcval, pc, &ep, 1, NULL)) {
6067      do_charge(un, cl, pcval);
6068    } else {
6069      /*	Close connection on malformed request
6070      */
6071      if ((NULL == job->soa) && (0 == job->szsoa)) {
6072        close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
6073      }
6074    }
6075  } else {
6076    /*	Close connection on malformed request
6077    */
6078    if ((NULL == job->soa) && (0 == job->szsoa)) {
6079      close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
6080    }
6081  }
6082  $? "- rq_acct_charge"
6083}
6084
6085
6086
6087/**	Object for DB traversal in control requests.
6088*/
6089typedef struct {
6090  dk4_sto_t	*s;	/**< Key name storage. */
6091  dk4_sto_it_t	*i;	/**< Key name storage iterator. */
6092  char		*cn;	/**< Class name. */
6093  char		*un;	/**< User name. */
6094} pqd_control_t;
6095
6096
6097
6098/**	Comparison function for storage.
6099	@param	l	Left object.
6100	@param	r	Right object.
6101	@param	cr	Comparison criteria (ignored).
6102	@return	Comparison result.
6103*/
6104static
6105int
6106db_entry_compare(const void *l, const void *r, int DK4_ARG_UNUSED(cr) )
6107{
6108  int		 back	= 0;
6109  DK4_UNUSED_ARG(cr)
6110  if (NULL != l) {
6111    if (NULL != r) {
6112      back = strcmp((const char *)l, (const char *)r);
6113      if (-1 > back) back = -1;
6114      if ( 1 < back) back =  1;
6115    } else {
6116      back = 1;
6117    }
6118  } else {
6119    if (NULL != r) {
6120      back = -1;
6121    }
6122  }
6123  return back;
6124}
6125
6126
6127
6128/**	Traverse simple database containing text.
6129	@param	obj	Object to modify during traversal.
6130	@param	k	Key text.
6131	@param	v	Value text.
6132	@return	1 on success, 0 on error (continue traversal),
6133		-1 on error (abort).
6134*/
6135static
6136int
6137rq_c_reset_traverse(void *obj, const char *k, const char * DK4_ARG_UNUSED(v) )
6138{
6139  pqd_control_t	*ctoptr	= NULL;		/* Traversal object pointer */
6140  char		*p1	= NULL;		/* DB entry type */
6141  char		*p2	= NULL;		/* Class name */
6142  char		*p3	= NULL;		/* User name */
6143  int		 clm	= 0;		/* Flag: Class name matches */
6144  int		 unm	= 0;		/* Flag: User name matches */
6145  int		 back	= 1;
6146  $? "+ rq_c_reset_traverse \"%s\"=\"%s\"", TR_8STR(k), TR_8STR(v)
6147  DK4_UNUSED_ARG(v)
6148  ctoptr = (pqd_control_t *)obj;
6149  if (0 != dk4str8_cpy_s(kb, sizeof(kb), k, NULL)) {
6150    p1 = dk4str8_start(kb, NULL);
6151    if (NULL != p1) {
6152      p2 = strchr(p1, ':');
6153      if (NULL != p2) {
6154        *(p2++) = '\0';
6155	p2 = dk4str8_start(p2, NULL);
6156      }
6157      if (NULL != p2) {
6158	if (0 == strcmp(p1, printqd_kw[152])) {
6159          p3 = strchr(p2, ':');
6160	  if (NULL != p3) {
6161	    *(p3++) = '\0';
6162	    p3 = dk4str8_start(p3, NULL);
6163	  }
6164	  if (NULL != p3) {
6165	    if (NULL != ctoptr->cn) {
6166	      if (0 == strcmp(ctoptr->cn, p2)) {
6167	        clm = 1;
6168	      }
6169	    } else {
6170	      clm = 1;
6171	    }
6172	    if (0 != clm) {
6173	      if (NULL != ctoptr->un) {
6174	        if (0 == strcmp(ctoptr->un, p3)) {
6175		  unm = 1;
6176		}
6177	      } else {
6178	        unm = 1;
6179	      }
6180	      if (0 != unm) {
6181	        p1 = dk4str8_dup(k, NULL);
6182		if (NULL != p1) {
6183		  if (0 == dk4sto_add(ctoptr->s, p1, NULL)) {
6184		    dk4mem_free(p1);		$? "! sto add"
6185		  }
6186		}
6187#if TRACE_DEBUG
6188		else {				$? "! insufficient memory"
6189		}
6190#endif
6191	      }
6192#if TRACE_DEBUG
6193	      else {				$? "! user name mismatch"
6194	      }
6195#endif
6196	    }
6197#if TRACE_DEBUG
6198	    else {				$? "! class name mismatch"
6199	    }
6200#endif
6201	  }
6202#if TRACE_DEBUG
6203	  else {				$? "! no third component"
6204	  }
6205#endif
6206	}
6207#if TRACE_DEBUG
6208	else {					$? "! not a p entry"
6209	}
6210#endif
6211      }
6212#if TRACE_DEBUG
6213      else {					$? "! no second component"
6214      }
6215#endif
6216    }
6217#if TRACE_DEBUG
6218    else {					$? "! empty"
6219    }
6220#endif
6221  }
6222  if (0 == can_continue(0, 0)) { back = -1; }
6223  $? "- rq_c_reset_traverse %d", back
6224  return back;
6225}
6226
6227
6228
6229/**	Traverse simple database containing text.
6230	@param	obj	Object to modify during traversal.
6231	@param	k	Key text.
6232	@param	v	Value text.
6233	@return	1 on success, 0 on error (continue traversal),
6234		-1 on error (abort).
6235*/
6236static
6237int
6238rc_c_dbcl_traverse(void *obj, const char *k, const char * DK4_ARG_UNUSED(v) )
6239{
6240  pqd_control_t	*pctobj;
6241  pqd_class_t	*cl;
6242  char		*p1;		/* First argument */
6243  char		*p2;		/* Second argument */
6244  int		 cdel	= 0;	/* Flag: Can delete item */
6245  int		 back	= 1;
6246
6247  DK4_UNUSED_ARG(v)
6248  if (0 != dk4str8_cpy_s(kb, sizeof(kb), k, NULL)) {
6249    if (0 != dk4str8_cpy_s(vb, sizeof(vb), k, NULL)) {
6250      p1 = strchr(kb, ':');
6251      if (NULL != p1) {
6252        *(p1++) = '\0';
6253	p1 = dk4str8_start(p1, NULL);
6254	dk4str8_normalize(kb, NULL);
6255	if (NULL != p1) {
6256	  pctobj = (pqd_control_t *)obj;
6257	  switch ( dk4str8_array_index(db_type_names, kb, 0) ) {
6258	    case 0 :	/* p:class:user */
6259	    case 1 :	/* a:class:user */
6260	    {
6261	      p2 = strchr(p1, ':');
6262	      if (NULL != p2) {
6263	        *(p2++) = '\0';
6264		p2 = dk4str8_start(p2, NULL);
6265		if (NULL != p2) {
6266		  dk4str8_normalize(p1, NULL);
6267		  dk4str8_normalize(p2, NULL);
6268		  cl = (pqd_class_t *)dk4sto_it_find_like(conf.i_c, p1, 1);
6269		  if (NULL != cl) {
6270		    if (NULL == dk4sto_it_find_like(cl->i_u, p2, 1)) {
6271		      if (NULL == getpwnam(p2)) {
6272		        cdel = 1;
6273		      }
6274		    }
6275		  } else {
6276		    cdel = 1;
6277		  }
6278		  if (0 != cdel) {
6279		    p1 = dk4str8_dup(vb, NULL);
6280		    if (NULL != p1) {
6281		      if (0 == dk4sto_add(pctobj->s, p1, NULL)) {
6282		        dk4mem_free(p1);
6283			back = 0;
6284		      }
6285		    } else {
6286		      back = 0;
6287		    }
6288		  }
6289		}
6290	      }
6291	    } break;
6292	    case 2 : {	/* j:printer */
6293	      dk4str8_normalize(p1, NULL);
6294	      if (NULL == get_printer_for_name(p1)) {
6295	        p1 = dk4str8_dup(vb, NULL);
6296		if (NULL != p1) {
6297		  if (0 == dk4sto_add(pctobj->s, p1, NULL)) {
6298		    dk4mem_free(p1);
6299		    back = 0;
6300		  }
6301		} else {
6302		  back = 0;
6303		}
6304	      }
6305	    } break;
6306	  }
6307	}
6308      }
6309    }
6310  }
6311  if (0 == can_continue(0, 0)) { back = -1; }
6312  return back;
6313}
6314
6315
6316
6317/**	Process control reset request.
6318	@param	job	Arguments for request.
6319*/
6320static
6321void
6322rq_c_reset(pqd_rq_t *job)
6323{
6324  pqd_control_t	 ctobj;
6325  char		*pc;
6326  char		*pn;
6327  char		*pv;
6328  $? "+ rq_c_reset"
6329  /*	Initialize variable
6330  */
6331  ctobj.s  = NULL;
6332  ctobj.i  = NULL;
6333  ctobj.cn = NULL;
6334  ctobj.un = NULL;
6335  /*	Process command arguments
6336  */
6337  pc = job->argptr;
6338  while (NULL != pc) {
6339    pn = dk4str8_next(pc, NULL);
6340    $? ". pc=\"%s\" pn=\"%s\"", pc, TR_8STR(pn)
6341    pv = dk4str8_chr(pc, '=');
6342    if (NULL != pv) {
6343      *(pv++) = '\0';
6344      pv = dk4str8_start(pv, NULL);
6345    }
6346    $? ". pc=\"%s\" pv=\"%s\"", pc, TR_8STR(pv)
6347    if (NULL != pv) {
6348      switch ( dk4str8_abbr_index(control_arg_names, '$', pc, 0) ) {
6349        case 0 : {
6350	  ctobj.cn = pv;
6351	} break;
6352	case 1 : {
6353	  ctobj.un = pv;
6354	} break;
6355	default : {
6356	  $? ". unknown \"%s\"=\"%s\"", pc, pv
6357	} break;
6358      }
6359    }
6360    pc = pn;
6361  }
6362  $? ". class=%s user=%s", TR_8STR(ctobj.cn), TR_8STR(ctobj.un)
6363  if (NULL != ctobj.cn) {
6364    if (0 == dk4str8_cmp(printqd_kw[2], ctobj.cn)) { ctobj.cn = NULL; }
6365  }
6366  if (NULL != ctobj.un) {
6367    if (0 == dk4str8_cmp(printqd_kw[2], ctobj.un)) { ctobj.un = NULL; }
6368  }
6369  ctobj.s = dk4sto_open(NULL);
6370  if (NULL != ctobj.s) {		$? ". storage"
6371    dk4sto_set_comp(ctobj.s, db_entry_compare, 0);
6372    ctobj.i = dk4sto_it_open(ctobj.s, NULL);
6373    if (NULL != ctobj.i) {		$? ". iterator"
6374      (void)dk4dbi_c8_traverse(db, (void *)(&ctobj), rq_c_reset_traverse);
6375      $? ". traversal finished"
6376      dk4sto_it_reset(ctobj.i);
6377      do {
6378        pc = (char *)dk4sto_it_next(ctobj.i);
6379        if (NULL != pc) {		$? ". delete \"%s\"", pc
6380          if (0 != can_continue(0, 0)) {
6381            (void)dbi_del(db, pc, NULL);
6382          }
6383          dk4mem_free(pc);
6384        }
6385      } while (NULL != pc);
6386      dk4sto_it_close(ctobj.i);
6387    }
6388#if TRACE_DEBUG
6389    else {				$? "! iterator"
6390    }
6391#endif
6392    dk4sto_close(ctobj.s);
6393  }
6394#if TRACE_DEBUG
6395  else {				$? "! storage"
6396  }
6397#endif
6398  $? "- rq_c_reset"
6399}
6400
6401
6402
6403/**	Process control add request.
6404	@param	job	Arguments for request.
6405*/
6406static
6407void
6408rq_c_add(pqd_rq_t *job)
6409{
6410  pqd_account_t	 oldacc;
6411  pqd_account_t	 newacc;
6412  pqd_class_t	*cl	= NULL;
6413  const char	*ep	= NULL;
6414  char		*clname	= NULL;
6415  char		*uname	= NULL;
6416  char		*ptext	= NULL;
6417  char		*pc;
6418  char		*pn;
6419  char		*pv;
6420  dk4_um_t	 pages	= (dk4_um_t)0UL;
6421
6422  /*	Process command arguments
6423  */
6424  pc = job->argptr;
6425  while (NULL != pc) {
6426    pn = dk4str8_next(pc, NULL);
6427    pv = dk4str8_chr(pc, '=');
6428    if (NULL != pv) {
6429      *(pv++) = '\0';
6430      pv = dk4str8_start(pv, NULL);
6431    }
6432    if (NULL != pv) {
6433      switch ( dk4str8_abbr_index(control_arg_names, '$', pc, 0) ) {
6434        case 0 : {
6435	  clname = pv;
6436	} break;
6437	case 1 : {
6438	  uname = pv;
6439	} break;
6440	case 2 : {
6441	  ptext = pv;
6442	} break;
6443      }
6444    }
6445    pc = pn;
6446  }
6447  if ((NULL != clname) && (NULL != uname) && (NULL != ptext)) {
6448    if (0 != dk4ma_input_c8_dec_dk4_um_t(&pages, ptext, &ep, 1, NULL)) {
6449      cl = (pqd_class_t *)dk4sto_it_find_like(conf.i_c, clname, 1);
6450      if (NULL != cl) {
6451        if (0 != retrieve_account_for_user_and_class(&oldacc, uname, cl, 1)) {
6452	  DK4_MEMCPY(&newacc, &oldacc, sizeof(pqd_account_t));
6453	  if ((DK4_UM_MAX - pages) >= newacc.account) {
6454	    newacc.account += pages;
6455	  } else {
6456	    newacc.account =  DK4_UM_MAX;
6457	  }
6458	  log_old_and_new_account(uname, cl, &oldacc, &newacc, pages, 1);
6459	  if (0 == save_account_for_user_and_class(&oldacc,&newacc,uname,cl)) {
6460	    /* ERROR: Failed to save new account */
6461	    log_5(NULL, 0UL, LOG_EMERG, 1, 156, 157, 158, uname, cl->name);
6462	  }
6463	} else {
6464	  /* ERROR: Failed to retrieve account data */
6465	  log_5(NULL, 0UL, LOG_EMERG, 1, 161, 162, 163, uname, cl->name);
6466	}
6467      } else {
6468        /* ERROR: No such class */
6469	log_3(NULL, 0UL, LOG_EMERG, 1, 164, 165, clname);
6470      }
6471    } else {
6472      /* ERROR: ptext not numeric */
6473      log_3(NULL, 0UL, LOG_EMERG, 1, 166, 167, ptext);
6474    }
6475  }
6476}
6477
6478
6479
6480/**	Process control database-cleanup request.
6481	@param	job	Arguments for request.
6482*/
6483static
6484void
6485rq_c_dbcl(void)
6486{
6487  pqd_control_t		 ctobj;
6488  char			*ptr;
6489
6490  DK4_MEMRES(&ctobj, sizeof(pqd_control_t));
6491  ctobj.s  = NULL;
6492  ctobj.i  = NULL;
6493  ctobj.cn = NULL;
6494  ctobj.un = NULL;
6495
6496  ctobj.s = dk4sto_open(NULL);
6497  if (NULL != ctobj.s) {
6498    dk4sto_set_comp(ctobj.s, db_entry_compare, 0);
6499    ctobj.i = dk4sto_it_open(ctobj.s, NULL);
6500    if (NULL != ctobj.i) {
6501      (void)dk4dbi_c8_traverse(db, (void *)(&ctobj), rc_c_dbcl_traverse);
6502      dk4sto_it_reset(ctobj.i);
6503      do {
6504        ptr = (char *)dk4sto_it_next(ctobj.i);
6505	if (NULL != ptr) {
6506	  if (0 != can_continue(0, 0)) {
6507	    dbi_del(db, ptr, NULL);
6508	  }
6509	  dk4mem_free(ptr);
6510	}
6511      } while(NULL != ptr);
6512      dk4sto_it_close(ctobj.i);
6513    }
6514    dk4sto_close(ctobj.s);
6515  }
6516}
6517
6518
6519
6520/**	Process control request.
6521	@param	job	Data for one request to process.
6522*/
6523static
6524void
6525rq_control(pqd_rq_t *job)
6526{
6527  char	*subcmd;
6528  int	 sc;
6529  $? "+ rq_control"
6530  subcmd = job->argptr;
6531  job->argptr = dk4str8_next(subcmd, NULL);
6532  $? ". argptr = \"%s\"", TR_8STR(job->argptr)
6533  switch ( (sc = dk4str8_abbr_index(control_sub_cmds, '$', subcmd, 0)) ) {
6534    case 0 : {		$? ". reset"
6535      rq_c_reset(job);
6536    } break;
6537    case 1 : {		$? ". add"
6538      rq_c_add(job);
6539    } break;
6540    case 2 : {		$? ". database-cleanup"
6541      rq_c_dbcl();
6542    } break;
6543    default : {
6544      /* ERROR: Illegal sub-command */
6545      log_3(NULL, 0UL, LOG_EMERG, 1, 168, 169, subcmd);
6546    } break;
6547  }
6548  $? "- rq_control"
6549}
6550
6551
6552
6553/**	Process contents of the inbuf buffer, send response if necessary.
6554	@param	job	Data for one request to process.
6555*/
6556static
6557void
6558process_data(pqd_rq_t *job)
6559{
6560  const char *	msgs[3];
6561  $? "+ process_data sock=%d level=%d addr=%s", job->sock, job->protlev, TR_8PTR(job->soa)
6562  job->cmdptr = dk4str8_start(inbuf, NULL);
6563  if (NULL != job->cmdptr) {
6564    dk4str8_delnl(job->cmdptr);
6565    /* LOG request */
6566    if (0 == conf.linf) {
6567      if ('i' == (job->cmdptr)[0]) {
6568        if ('n' == (job->cmdptr)[1]) {
6569	  if ('f' == (job->cmdptr)[2]) {
6570	    if ('o' == (job->cmdptr)[3]) {
6571	      job->logthis = 0;
6572	    }
6573	  }
6574	}
6575      }
6576    }
6577    if (0 != job->logthis) {
6578      msgs[0] = printqd_kw[87];
6579      msgs[1] = job->cmdptr;
6580      msgs[2] = NULL;
6581      log_multipart_message(NULL, 0UL, LOG_EMERG, 0, msgs, 2);
6582    }
6583    /* Process request */
6584    job->argptr = dk4str8_next(job->cmdptr, NULL);
6585    job->action = dk4str8_array_index(pqd_command_names, job->cmdptr, 0);
6586    if (0 != protocol_level_check(job->action, job->protlev)) {
6587      switch (job->action) {
6588        case PQD_CMD_INFO : {
6589	  rq_info(job);
6590	} break;
6591        case PQD_CMD_JOBSTART : {
6592	  rq_jobstart(job);
6593	} break;
6594	case PQD_CMD_FILESTART : {
6595	  rq_filestart(job);
6596	} break;
6597	case PQD_CMD_FILEEND : {
6598	  rq_fileend(job);
6599	} break;
6600	case PQD_CMD_ACCT_CHECK : {
6601	  rq_acct_check(job);
6602	} break;
6603	case PQD_CMD_ACCT_START : {
6604	  rq_acct_start(job);
6605	} break;
6606	case PQD_CMD_ACCT_END : {
6607	  rq_acct_end(job);
6608	} break;
6609	case PQD_CMD_ACCT_CHARGE : {
6610	  rq_acct_charge(job);
6611	} break;
6612	case PQD_CMD_CONTROL : {
6613	  rq_control(job);
6614	} break;
6615      }
6616    } else {
6617      /*	Close connection if request is denied.
6618      */
6619      if ((NULL == job->soa) && (0 == job->szsoa)) {
6620        close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn);
6621      }
6622    }
6623  }
6624  $? "- process_data"
6625}
6626
6627
6628
6629/**	Run one pass within inner loop.
6630*/
6631static
6632void
6633one_pass(void)
6634{
6635  fd_set			 rfds;		/* Read file desc set */
6636  dk4_sockaddr_storage_t	 soa_sto;	/* Remote address */
6637  pqd_rq_t			 job;		/* Job structure */
6638  struct sockaddr		*soa;		/* Remote address */
6639#if VERSION_BEFORE_20160329
6640  struct sockaddr_in		*soa4;		/* Remote IPv4 address */
6641#if DK4_HAVE_STRUCT_SOCKADDR_IN6
6642  struct sockaddr_in6		*soa6;		/* Remote IPv6 address */
6643#endif
6644#endif
6645  pqd_l_conn_t			*pconnloc;	/* UNIX connection record */
6646  pqd_n_conn_t			*pconntcp;	/* TCP connection record */
6647  size_t			 sz_soa_sto;	/* Address size */
6648  size_t			 rdbytes;	/* Number of bytes read */
6649  size_t			 i;		/* Traverse socket set */
6650  dk4_socket_t			 sock;		/* Current new socket */
6651  int				 maxfd  = 0;	/* Maximum fd number */
6652  int				 l_conn	= 0;	/* Flag: Local connection */
6653  int				 l_proc	= 0;	/* Flag: Local processing */
6654  int				 res	= 0;	/* Operation result */
6655  int				 addrok;	/* Flag: Address size ok */
6656  int				 protlev;	/* Procotol level for conn */
6657
6658  $? "+ one_pass"
6659  /*	Create file descriptor set for select.
6660  */
6661  FD_ZERO(&rfds);
6662  /*	Check for incoming UNIX conn rq only if maximum not yet reached.
6663  */
6664  if ((0 == conf.m_loc) || (con_loc < conf.m_loc)) {
6665    FD_SET(so_unix,&rfds);
6666    if (maxfd < so_unix) { maxfd = so_unix; }
6667  }
6668
6669  /*	Check for incoming TCP conn rq only if maximum not yet reached.
6670  */
6671  if ((NULL != ss_tcp) && ((0 == conf.m_tcp) || (con_tcp < conf.m_tcp))) {
6672    for (i = 0; i < ss_tcp->szUsed; i++) {
6673      FD_SET((ss_tcp->pSockets)[i],&rfds);
6674      if (maxfd < (ss_tcp->pSockets)[i]) { maxfd = (ss_tcp->pSockets)[i]; }
6675    }
6676  }
6677
6678  /*	Check for data on existing UNIX connections.
6679  */
6680  dk4sto_it_reset(i_c_unix);
6681  do {
6682    pconnloc = (pqd_l_conn_t *)dk4sto_it_next(i_c_unix);
6683    if (NULL != pconnloc) {
6684      FD_SET(pconnloc->sock,&rfds);
6685      if (maxfd < pconnloc->sock) { maxfd = pconnloc->sock; }
6686    }
6687  } while (NULL != pconnloc);
6688
6689  /*	Check for data on existing TCP connections.
6690  */
6691  dk4sto_it_reset(i_c_tcp);
6692  do {
6693    pconntcp = (pqd_n_conn_t *)dk4sto_it_next(i_c_tcp);
6694    if (NULL != pconntcp) {
6695      FD_SET(pconntcp->sock,&rfds);
6696      if (maxfd < pconntcp->sock) { maxfd = pconntcp->sock; }
6697    }
6698  } while (NULL != pconntcp);
6699
6700  /*	Check for data on UDP sockets.
6701  */
6702  if (NULL != ss_udp) {
6703    for (i = 0; i < ss_udp->szUsed; i++) {
6704      FD_SET((ss_udp->pSockets)[i],&rfds);
6705      if (maxfd < (ss_udp->pSockets)[i]) { maxfd = (ss_udp->pSockets)[i]; }
6706    }
6707  }
6708
6709  /*	Check for signal meanwhile.
6710  */
6711  if (0 == can_continue(1, 0)) {		$? ". one_pass: interrupted"
6712    goto finished;
6713  }
6714
6715  /*	Run select().
6716  */
6717  errno = 0;
6718  res = select((1+maxfd), &rfds, NULL, NULL, NULL);
6719  if (-1 == res) {
6720    switch (errno) {
6721      case EINTR: {		$? ". one_pass: select interrupted"
6722        goto finished;
6723      } break;
6724      default: {		$? ". one_pass: error from select"
6725        /* ERROR: Serious error from select() function */
6726	log_1(NULL, 0UL, LOG_EMERG, 1, 119);
6727	ccouter = -1;
6728	ccinner = -1;
6729	goto finished;
6730      } break;
6731    }
6732  }
6733  if (0 == res) {		$? ". one_pass: no descriptors ready"
6734    goto finished;
6735  }
6736
6737  /*	Check for new connection attempts on local socket.
6738  */
6739  if ((0 == conf.m_loc) || (con_loc < conf.m_loc)) {
6740    if (0 != (FD_ISSET(so_unix,&rfds))) {	$? ". one_pass: local conn"
6741      sock = dk4socket_accept(so_unix, NULL, NULL, NULL);
6742      if (INVALID_SOCKET != sock) {
6743        pconnloc = dk4mem_new(pqd_l_conn_t,1,NULL);
6744	if (NULL != pconnloc) {
6745	  pconnloc->sock = sock;
6746	  if (dk4sto_add(s_c_unix, pconnloc, NULL)) {
6747	    con_loc++;
6748	    l_conn = 1;
6749	  } else {
6750	    dk4mem_free(pconnloc);
6751	    (void)dk4socket_close(sock, NULL);
6752	  }
6753	} else {
6754	  (void)dk4socket_close(sock, NULL);
6755	  /* ERROR: Memory */
6756	  log_1(NULL, 0UL, LOG_EMERG, 1, 120);
6757	  ccouter = -1;
6758	  ccinner = -1;
6759	}
6760      }
6761    }
6762  }
6763
6764  /*	Process data from local socket connections.
6765  */
6766  dk4sto_it_reset(i_c_unix);
6767  do {
6768    if (0 != can_continue(1, 0)) {
6769      pconnloc = (pqd_l_conn_t *)dk4sto_it_next(i_c_unix);
6770      if (NULL != pconnloc) {
6771        if (0 != (FD_ISSET(pconnloc->sock,&rfds))) {
6772	  l_proc = 1;				$? ". one_pass: local data"
6773	  rdbytes = sizeof(inbuf);
6774	  res = dk4socket_recv(
6775	    pconnloc->sock, inbuf, &rdbytes, 0, 0L, 0L, NULL
6776	  );
6777	  if (0 != can_continue(0, 0)) {
6778	    if (DK4_SOCKET_RESULT_SUCCESS == res) {
6779	      if (0 < rdbytes) {
6780	        if (sizeof(inbuf) > rdbytes) {
6781	          inbuf[rdbytes] = '\0';
6782		  job_init(&job);
6783		  job.sock    = pconnloc->sock;
6784		  job.protlev = PQD_PROTO_ADMIN;
6785		  job.connptr = pconnloc;
6786		  job.sto     = s_c_unix;
6787		  job.pnconn  = &con_loc;
6788		  process_data(&job);
6789	        }
6790	      } else {
6791	        close_stream_conn(pconnloc->sock, s_c_unix, pconnloc, &con_loc);
6792	      }
6793	    } else {
6794	      close_stream_conn(pconnloc->sock, s_c_unix, pconnloc, &con_loc);
6795	    }
6796	  } else {
6797	    pconnloc = NULL;
6798	  }
6799        }
6800      }
6801    } else {
6802      pconnloc = NULL;
6803    }
6804  } while (NULL != pconnloc);
6805
6806  /*	Skip TCP and UDP data if activity on higher priorized local sockets.
6807  */
6808  if ((0 != l_conn) || (0 != l_proc) || (0 == can_continue(1, 0))) {
6809    $? ". one_pass: skip non-local processing"
6810    goto finished;
6811  }
6812
6813  /*	Check for new connection attempts on TCP.
6814  */
6815  if ((NULL != ss_tcp) && ((0 == conf.m_tcp) || (con_tcp < conf.m_tcp))) {
6816    for (i = 0; ((i < ss_tcp->szUsed) && (0 != can_continue(1, 0))); i++) {
6817      if (0 != (FD_ISSET((ss_tcp->pSockets)[i],&rfds))) {
6818        $? ". one_pass: tcp conn"
6819        DK4_MEMRES(&soa_sto, sizeof(soa_sto));
6820	sz_soa_sto = sizeof(soa_sto);
6821	sock = dk4socket_accept(
6822	  (ss_tcp->pSockets)[i],
6823	  (struct sockaddr *)(&soa_sto), &sz_soa_sto, NULL
6824	);
6825	if (INVALID_SOCKET != sock) {
6826#if VERSION_BEFORE_20160329
6827#if DK4_HAVE_STRUCT_SOCKADDR_IN6
6828	  soa6 = (struct sockaddr_in6 *)(&soa_sto);
6829#endif
6830	  soa4 = (struct sockaddr_in  *)(&soa_sto);
6831#endif
6832	  soa  = (struct sockaddr     *)(&soa_sto);
6833	  addrok = 0;
6834	  if (AF_INET == soa->sa_family) {
6835	    if (sizeof(struct sockaddr_in) == sz_soa_sto) {
6836	      addrok = 1;
6837	    }
6838	  }
6839#if DK4_HAVE_STRUCT_SOCKADDR_IN6
6840	  if (AF_INET6 == soa->sa_family) {
6841	    if (sizeof(struct sockaddr_in6) == sz_soa_sto) {
6842	      addrok = 1;
6843	    }
6844	  }
6845#endif
6846	  if (0 != addrok) {
6847	    protlev = protocol_level_for_address(soa);
6848	    if (PQD_PROTO_NONE < protlev) {	$? ". allowed %d", protlev
6849	      pconntcp = dk4mem_new(pqd_n_conn_t,1,NULL);
6850	      if (NULL != pconntcp) {
6851	        pconntcp->sock = sock;
6852		if (0 != dk4sto_add(s_c_tcp, pconntcp, NULL)) {
6853#if VERSION_BEFORE_20160811
6854		  DK4_MEMCPY(&(pconntcp->raddr),&soa_sto,sizeof(&soa_sto));
6855#else
6856		  DK4_MEMCPY(&(pconntcp->raddr),&soa_sto,sizeof(soa_sto));
6857#endif
6858		  pconntcp->pqdpl = protlev;
6859		} else {
6860		  dk4mem_free(pconntcp);
6861		  (void)dk4socket_close(sock, NULL);
6862		  /* ERROR: Memory */
6863		  log_1(NULL, 0UL, LOG_EMERG, 1, 120);
6864		  ccouter = -1;
6865		  ccinner = -1;
6866		}
6867	      } else {
6868	        (void)dk4socket_close(sock, NULL);
6869	        /* ERROR: Memory */
6870		log_1(NULL, 0UL, LOG_EMERG, 1, 120);
6871		ccouter = -1;
6872		ccinner = -1;
6873	      }
6874	    } else {				$? "! denied client"
6875	      (void)dk4socket_close(sock, NULL);
6876	    }
6877	  } else {				$? "! size/family mismatch"
6878	    (void)dk4socket_close(sock, NULL);
6879	  }
6880	}
6881      }
6882    }
6883  }
6884  if (0 == can_continue(1, 0)) {
6885    $? ". one_pass: interrupted"
6886    goto finished;
6887  }
6888
6889  /*	Process data from TCP connections.
6890  */
6891  dk4sto_it_reset(i_c_tcp);
6892  do {
6893    if (0 != can_continue(1, 0)) {
6894      pconntcp = (pqd_n_conn_t *)dk4sto_it_next(i_c_tcp);
6895      if (NULL != pconntcp) {
6896        if (0 != (FD_ISSET(pconntcp->sock,&rfds))) {
6897	  $? ". one_pass: tcp data"
6898	  rdbytes = sizeof(inbuf);
6899	  res = dk4socket_recv(
6900	    pconntcp->sock, inbuf, &rdbytes, 0, 0L, 0L, NULL
6901	  );
6902	  if (0 != can_continue(0, 0)) {
6903	    if (DK4_SOCKET_RESULT_SUCCESS == res) {
6904	      if (0 < rdbytes) {
6905	        if (sizeof(inbuf) > rdbytes) {
6906	          inbuf[rdbytes] = '\0';
6907		  job_init(&job);
6908		  job.sock    = pconntcp->sock;
6909		  job.protlev = pconntcp->pqdpl;
6910		  job.connptr = pconntcp;
6911		  job.sto     = s_c_tcp;
6912		  job.pnconn  = &con_tcp;
6913		  process_data(&job);
6914	        }
6915	      } else {
6916	        close_stream_conn(pconntcp->sock, s_c_tcp, pconntcp, &con_tcp);
6917	      }
6918	    } else {
6919	      close_stream_conn(pconntcp->sock, s_c_tcp, pconntcp, &con_tcp);
6920	    }
6921	  } else {
6922	  }
6923	}
6924      }
6925    } else {
6926      pconntcp = NULL;
6927    }
6928  } while (NULL != pconntcp);
6929  if (0 == can_continue(1, 0)) {
6930    $? ". one_pass: interrupted"
6931    goto finished;
6932  }
6933
6934  /*	Process UDP data.
6935  */
6936  if (NULL != ss_udp) {
6937    for (i = 0; ((i < ss_udp->szUsed) && (0 != can_continue(1, 0))); i++) {
6938      if (0 != (FD_ISSET((ss_udp->pSockets)[i],&rfds))) {
6939        $? ". one_pass: udp data"
6940        rdbytes = sizeof(inbuf);
6941        sz_soa_sto = sizeof(soa_sto);
6942        res = dk4socket_recvfrom(
6943          (ss_udp->pSockets)[i], inbuf, &rdbytes, 0,
6944	  (struct sockaddr *)(&soa_sto), &sz_soa_sto, 0L, 0L, NULL
6945        );
6946        if ((DK4_SOCKET_RESULT_SUCCESS == res) && (0 < rdbytes)) {
6947          if ((0 != can_continue(1, 0)) && (sizeof(inbuf) > rdbytes)) {
6948	    inbuf[rdbytes] = '\0';
6949#if VERSION_BEFORE_20160329
6950#if DK4_HAVE_STRUCT_SOCKADDR_IN6
6951	    soa6 = (struct sockaddr_in6 *)(&soa_sto);
6952#endif
6953	    soa4 = (struct sockaddr_in  *)(&soa_sto);
6954#endif
6955	    soa  = (struct sockaddr     *)(&soa_sto);
6956	    addrok = 0;
6957	    if (AF_INET == soa->sa_family) {
6958	      if (sizeof(struct sockaddr_in) == sz_soa_sto) {
6959	        addrok = 1;
6960	      }
6961	    }
6962#if DK4_HAVE_STRUCT_SOCKADDR_IN6
6963	    if (AF_INET6 == soa->sa_family) {
6964	      if (sizeof(struct sockaddr_in6) == sz_soa_sto) {
6965	        addrok = 1;
6966	      }
6967	    }
6968#endif
6969	    if (0 != addrok) {
6970	      protlev = protocol_level_for_address(soa);
6971	      if (PQD_PROTO_NONE < protlev) {
6972	        job_init(&job);
6973	        job.sock    = (ss_udp->pSockets)[i];
6974	        job.protlev = PQD_PROTO_INFO;
6975	        job.soa     = (struct sockaddr *)(&soa_sto);
6976	        job.szsoa   = sz_soa_sto;
6977	        process_data(&job);
6978	      }
6979	    }
6980	  }
6981        }
6982      }
6983    }
6984  }
6985
6986  finished:
6987  $? ". end of pass, synchronize db"
6988  if (0 == dbi_sync(db, NULL)) {
6989    /* ERROR: Failed to synchronize database */
6990    log_1(NULL, 0UL, LOG_EMERG, 1, 121);
6991  }
6992  if (0 == can_continue(1, 0)) {
6993    ccinner = 0;
6994    if (0 == can_continue(0, 0)) {
6995      ccouter = 0;
6996    }
6997  }
6998  $? "- one_pass"
6999}
7000
7001
7002
7003
7004/**	Run two nested loops: The outer loop is only finished
7005	by SIGTERM or SIGINT to exit the program, the inner loop
7006	is also finished by SIGHUP to re-read the configuration.
7007*/
7008static
7009void
7010service_loops(void)
7011{
7012  $!trace-init	/tmp/printqd.deb
7013  $? "+ service_loops"
7014  /* LOG: Daemon running */
7015  log_1(NULL, 0UL, LOG_INFO, 0, 73);
7016  if (outer_allocate_resources()) {
7017    /*	Outer loop is left on SIGTERM or SIGINT.
7018    */
7019    while(1 == ccouter) {
7020      if (1 == can_continue(0, 0)) {
7021        config_init();
7022        if (0 != config_read()) {
7023          if (inner_allocate_resources()) {
7024	    if (0 != change_group_and_user()) {
7025	      /*	Inner loop is left on SIGHUP, SIGTERM or SIGINT.
7026	      */
7027              ccinner = 1;
7028	      *sig_pass_pointer(&sig_had_hup) = 0;
7029	      prev_filelog_failed = 0;
7030	      /* LOG: Entering service mode */
7031	      log_1(NULL, 0UL, LOG_INFO, 0, 75);
7032              while(1 == ccinner) {
7033                if (1 == can_continue(1, 0)) {	$? ". yet another pass"
7034		  one_pass();
7035	        } else {				$? "! signal"
7036	          ccinner = 0;
7037	          if (0 == can_continue(0, 0)) {
7038		    ccouter = 0;
7039		    /* LOG: Exiting service mode */
7040		    log_1(NULL, 0UL, LOG_INFO, 0, 76);
7041		  } else {
7042		    /* LOG: Exiting service mode for reconfiguration */
7043		    log_1(NULL, 0UL, LOG_INFO, 0, 77);
7044		  }
7045	        }
7046              }
7047	      /* LOG: Exited service mode */
7048	      log_1(NULL, 0UL, LOG_INFO, 0, 78);
7049	    } else {				$? "! failed to change user"
7050	      ccouter = -1;
7051	    }
7052	  } else {				$? "! inner resource allocation"
7053	    ccouter = -1;
7054	  }
7055	  if (0 == inner_release_resources()) {	$? "! inner resource release"
7056	    ccouter = -1;
7057	  }
7058        } else {				$? "! configuration read"
7059          ccouter = -1;
7060        }
7061	config_release();
7062      } else {					$? "! signal"
7063        ccouter = 0;
7064      }
7065    }
7066  }
7067#if TRACE_DEBUG
7068  else {					$? "! outer resource allocation"
7069  }
7070#endif
7071  outer_release_resources();
7072  /* LOG: Daemon exiting */
7073  log_1(
7074    NULL, 0UL,
7075    ((0 == ccouter) ? (LOG_INFO) : (LOG_ERR)),
7076    ((0 == ccouter) ? (0) : (1)),
7077    ((0 == ccouter) ? (74) : (187))
7078  );
7079  $? "- service_loops"
7080  $!trace-end
7081}
7082
7083
7084
7085/**	Run service in debug mode or normal mode.
7086*/
7087static
7088void
7089set_signal_handlers_and_run_service(void)
7090{
7091  int			loop_was_running	= 0;
7092#ifdef	DK4_HAVE_SIGACTION
7093#ifdef SIGHUP
7094  struct sigaction	ohup;
7095  struct sigaction	nhup;
7096#endif
7097#ifdef SIGPIPE
7098  struct sigaction	opipe;
7099  struct sigaction	npipe;
7100#endif
7101  struct sigaction	oint;
7102  struct sigaction	nint;
7103  struct sigaction	oterm;
7104  struct sigaction	nterm;
7105#else
7106#ifdef SIGPIPE
7107  dk4_sig_handler_t	*opipe	= NULL;
7108#endif
7109#ifdef SIGHUP
7110  dk4_sig_handler_t	*ohup	= NULL;
7111#endif
7112  dk4_sig_handler_t	*oterm	= NULL;
7113  dk4_sig_handler_t	*oint	= NULL;
7114#endif
7115#ifdef	DK4_HAVE_SIGACTION
7116#ifdef SIGHUP
7117  int			shup	= 0;
7118#endif
7119#ifdef SIGPIPE
7120  int			spipe	= 0;
7121#endif
7122  int			sint	= 0;
7123  int			sterm	= 0;
7124  int			s_f_i	= 0;
7125  int			s_f_r	= 0;
7126#endif
7127
7128  /*	Set signal handlers
7129  */
7130#if DK4_HAVE_SIGACTION
7131#ifdef	SIGPIPE
7132  DK4_MEMRES(&npipe, sizeof(npipe));
7133  npipe.sa_handler = sig_handler_pipe;
7134  npipe.sa_flags = 0;
7135  if (0 != sigemptyset(&npipe.sa_mask)) {
7136    s_f_i = 1;
7137    goto finished;
7138  }
7139  if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) {
7140    s_f_i = 1;
7141    goto finished;
7142  }
7143  if (0 != sigaction(SIGPIPE, &npipe, &opipe)) {
7144    s_f_i = 1;
7145    goto finished;
7146  }
7147  spipe = 1;
7148#endif
7149#ifdef	SIGHUP
7150  DK4_MEMRES(&nhup, sizeof(nhup));
7151  nhup.sa_handler = sig_handler_hup;
7152  nhup.sa_flags = 0;
7153  if (0 != sigemptyset(&nhup.sa_mask)) {
7154    s_f_i = 1;
7155    goto finished;
7156  }
7157  if (0 != sigaddset(&nhup.sa_mask, SIGHUP)) {
7158    s_f_i = 1;
7159    goto finished;
7160  }
7161  if (0 != sigaction(SIGHUP, &nhup, &ohup)) {
7162    s_f_i = 1;
7163    goto finished;
7164  }
7165  shup = 1;
7166#endif
7167  DK4_MEMRES(&nterm, sizeof(nterm));
7168  nterm.sa_handler = sig_handler_term;
7169  nterm.sa_flags = 0;
7170  if (0 != sigemptyset(&nterm.sa_mask)) {
7171    s_f_i = 1;
7172    goto finished;
7173  }
7174  if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) {
7175    s_f_i = 1;
7176    goto finished;
7177  }
7178  if (0 != sigaction(SIGTERM, &nterm, &oterm)) {
7179    s_f_i = 1;
7180    goto finished;
7181  }
7182  sterm = 1;
7183  DK4_MEMRES(&nint, sizeof(nint));
7184  nint.sa_handler = sig_handler_int;
7185  nint.sa_flags = 0;
7186  if (0 != sigemptyset(&nint.sa_mask)) {
7187    s_f_i = 1;
7188    goto finished;
7189  }
7190  if (0 != sigaddset(&nint.sa_mask, SIGINT)) {
7191    s_f_i = 1;
7192    goto finished;
7193  }
7194  if (0 != sigaction(SIGINT, &nint, &oint)) {
7195    s_f_i = 1;
7196    goto finished;
7197  }
7198  sint = 1;
7199#else
7200#ifdef	SIGPIPE
7201  opipe = sigset(SIGPIPE, sig_handler_pipe);
7202#endif
7203#ifdef	SIGHUP
7204  ohup = sigset(SIGHUP, sig_handler_hup);
7205#endif
7206  oint  = sigset(SIGINT,  sig_handler_int);
7207  oterm = sigset(SIGTERM, sig_handler_term);
7208#endif
7209
7210
7211  service_loops();
7212  loop_was_running = 1;
7213
7214  finished:
7215
7216  /*
7217  */
7218  if (0 == loop_was_running) {
7219    log_1(NULL, 0UL, LOG_ERR, 1, 72);
7220  }
7221
7222  /*	Restore signal handlers
7223  */
7224#if DK4_HAVE_SIGACTION
7225  if (0 != s_f_i) {
7226    /* ERROR: Failed to install signal handlers */
7227    log_1(NULL, 0UL, LOG_EMERG, 1, 68);
7228    dk4dmt_error_sysfct(&dmt);
7229  }
7230  if (0 != sint) {
7231  if (0 != sigaction(SIGINT, &oint, NULL)) {
7232    s_f_r = 1;
7233  }
7234  }
7235  if (0 != sterm) {
7236  if (0 != sigaction(SIGTERM, &oterm, NULL)) {
7237    s_f_r = 1;
7238  }
7239  }
7240#ifdef	SIGHUP
7241  if (0 != shup) {
7242  if (0 != sigaction(SIGHUP, &ohup, NULL)) {
7243    s_f_r = 1;
7244  }
7245  }
7246#endif
7247#ifdef	SIGPIPE
7248  if (0 != spipe) {
7249  if (0 != sigaction(SIGPIPE, &opipe, NULL)) {
7250    s_f_r = 1;
7251  }
7252  }
7253#endif
7254  if (0 != s_f_r) {
7255    /* ERROR: Failed to restore signal handlers */
7256    log_1(NULL, 0UL, LOG_EMERG, 1, 69);
7257    dk4dmt_error_sysfct(&dmt);
7258  }
7259#else
7260  if (NULL != oterm) { sigset(SIGTERM, oterm); }
7261  if (NULL != oint ) { sigset(SIGINT,  oint ); }
7262#ifdef	SIGHUP
7263  if (NULL != ohup) { sigset(SIGHUP, ohup); }
7264#endif
7265#ifdef	SIGPIPE
7266  if (NULL != opipe) { sigset(SIGPIPE, opipe); }
7267#endif
7268#endif
7269
7270}
7271
7272
7273
7274/**	Program entry point.
7275	@param	argc
7276	@param	argv
7277	@return	0 on success, any other value indicates an error.
7278*/
7279int
7280main(int argc, char *argv[])
7281{
7282  pid_t		 cpid;		/* Child process PID */
7283  int		 res;		/* Operation result */
7284
7285  /*	Initialize daemon tool structure
7286  */
7287  dk4dmt_init(&dmt);
7288  dk4dmt_set_program(&dmt, printqd_kw[0]);
7289  dk4dmt_set_pidfile(&dmt, pid_file_name);
7290  dk4dmt_set_syslog_feature(&dmt, syslogf);
7291  /*
7292  	Process command line arguments.
7293  */
7294  res = dk4opt_process_argv(
7295    options, szoptions, argc, argv, NULL, NULL, 1, 1, NULL
7296  );
7297  if (0 == res) {
7298    dk4dmt_error_usage(&dmt);	/* Invalid or excess arguments */
7299    fputs("printqd: ERROR: Invalid command line arguments!\n", stderr);
7300    fflush(stderr);
7301    goto finished;
7302  }
7303
7304  /*	Must be started as root.
7305  */
7306  if (0 != geteuid()) {
7307    dk4dmt_error_usage(&dmt);
7308    fputs("printqd: ERROR: Must be run as root!\n", stderr);
7309    fflush(stderr);
7310    goto finished;
7311  }
7312
7313  /*	Check used command line arguments.
7314  */
7315  if (0 != options[0].found) { debug = 1; }
7316
7317  /*	Run in debug mode.
7318  */
7319  if (0 != debug) {
7320    dk4dmt_set_log_stderr(&dmt, 1);
7321    dk4dmt_success(&dmt);
7322    set_signal_handlers_and_run_service();
7323    goto finished;
7324  }
7325
7326  /*	PREPARE TO RUN AS DAEMON
7327  */
7328  dk4dmt_set_program(&dmt, printqd_kw[0]);
7329  dk4dmt_set_pidfile(&dmt, pid_file_name);
7330  dk4dmt_set_syslog_feature(&dmt, syslogf);
7331  if (0 == dk4dmt_parent_before_fork(&dmt)) {
7332    goto finished;
7333  }
7334
7335  /*	Create first background process
7336  */
7337  cpid = fork();
7338  if ((pid_t)0 < cpid) {
7339    /* In the parent process */
7340    dk4dmt_parent_after_fork(&dmt);
7341    goto finished;
7342  } else {
7343    if ((pid_t)0 == cpid) {
7344      /* In the child process, continue below */
7345    } else {
7346      /* Failed to create process */
7347      /* ERROR: fork() failed */
7348      fputs(printqd_kw[0], stderr);
7349      fputs(printqd_kw[70], stderr);
7350      fflush(stderr);
7351      dk4dmt_parent_fork_failed(&dmt);
7352      goto finished;
7353    }
7354  }
7355
7356  /*	Intermediate process and daemon process should indicate success.
7357  */
7358  dk4dmt_success(&dmt);		/* Intermediate process */
7359
7360  /*	Decouple from terminals.
7361  */
7362  if (0 == dk4dmt_intermediate_before_fork(&dmt)) {
7363    goto finished;
7364  }
7365
7366  /*	Create daemon background process from within first background process
7367  */
7368  cpid = fork();
7369  if ((pid_t)0 < cpid) {
7370    /* In the intermediate process */
7371    dk4dmt_intermediate_after_fork(&dmt);
7372    goto finished;
7373  } else {
7374    if ((pid_t)0 == cpid) {
7375      /* In the daemon process, continue below */
7376    } else {
7377      /* Failed to create process */
7378      /* ERROR: fork() failed */
7379      log_1(NULL, 0UL, LOG_ERR, 1, 71);
7380      dk4dmt_intermediate_fork_failed(&dmt);
7381      goto finished;
7382    }
7383  }
7384
7385  /*	In the daemon process
7386  */
7387  if (0 != dk4dmt_daemon_start(&dmt)) {
7388    set_signal_handlers_and_run_service();
7389  }
7390  dk4dmt_daemon_end(&dmt);
7391
7392  /*	Exit the program.
7393  */
7394  finished:
7395  exval = dk4dmt_get_exit_status(&dmt);
7396  exit(exval); return exval;
7397}
7398
7399#else
7400
7401/**	Program entry point.
7402	@param	argc
7403	@param	argv
7404	@return	0 on success, any other value indicates an error.
7405*/
7406int
7407main(int argc, char *argv[])
7408{
7409  fputs("printqd: ERROR: Build requirements violation!\n", stderr);
7410#if DK4_CHAR_SIZE > 1
7411  fputs("Default character size is larger than 1!\n", stderr);
7412#endif
7413#if	!((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET))
7414  fputs("Neither sigaction() nor sigset() are available!\n", stderr);
7415#endif
7416#if	!(DK4_HAVE_PWD_H)
7417  fputs("Include file \"pwd.h\" not found!\n", stderr);
7418#endif
7419#if	!(DK4_HAVE_UID_T)
7420  fputs("Data type uid_t not available!\n", stderr);
7421#endif
7422#if	!(DK4_HAVE_GRP_H)
7423  fputs("Include file \"grp.h\" not found!\n", stderr);
7424#endif
7425#if	!(DK4_HAVE_GID_T)
7426  fputs("Data type gid_t not available!\n", stderr);
7427#endif
7428#if	!(DK4_HAVE_MODE_T)
7429  fputs("Data type mode_t not available!\n", stderr);
7430#endif
7431#if	!(DK4_HAVE_GETPWNAM)
7432  fputs("Function getpwnam() is not available!\n", stderr);
7433#endif
7434#if	!(DK4_HAVE_GETGRNAM)
7435  fputs("Function getgrnam() is not available!\n", stderr);
7436#endif
7437#if	!((DK4_HAVE_SETSID) || (DK4_HAVE_SETPGRP))
7438  fputs("Neither setsid() nor setpgrp() are available!\n", stderr);
7439#endif
7440#if	DK4_ON_WINDOWS
7441  fputs("On Windows platform!\n", stderr);
7442#endif
7443  fflush(stderr);
7444  exit(EXIT_FAILURE); return EXIT_FAILURE;
7445}
7446
7447
7448#endif
7449