xref: /illumos-gate/usr/src/cmd/lp/lib/lp/alerts.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
32 
33 #include "stdio.h"
34 #include "string.h"
35 #include "errno.h"
36 #include "limits.h"
37 #include "unistd.h"
38 
39 #include "lp.h"
40 
41 extern char		**environ;
42 
43 static void		envlist(int, char **);
44 
45 /*
46  * We recognize the following key phrases in the alert prototype
47  * file, and replace them with appropriate values.
48  */
49 #define NALRT_KEYS	7
50 # define ALRT_ENV		0
51 # define ALRT_PWD		1
52 # define ALRT_ULIMIT		2
53 # define ALRT_UMASK		3
54 # define ALRT_INTERVAL		4
55 # define ALRT_CMD		5
56 # define ALRT_USER		6
57 
58 static struct {
59 	char			*v;
60 	short			len;
61 }			shell_keys[NALRT_KEYS] = {
62 #define	ENTRY(X)	X, sizeof(X)-1
63 	ENTRY("-ENVIRONMENT-"),
64 	ENTRY("-PWD-"),
65 	ENTRY("-ULIMIT-"),
66 	ENTRY("-UMASK-"),
67 	ENTRY("-INTERVAL-"),
68 	ENTRY("-CMD-"),
69 	ENTRY("-USER-"),
70 };
71 
72 /*
73  * These are used to bracket the administrator's command, so that
74  * we can find it easily. We're out of luck if the administrator
75  * includes an identical phrase in their command.
76  */
77 #define ALRT_CMDSTART "## YOUR COMMAND STARTS HERE -- DON'T TOUCH ABOVE!!"
78 #define ALRT_CMDEND   "## YOUR COMMAND ENDS HERE -- DON'T TOUCH BELOW!!"
79 
80 /**
81  ** putalert() - WRITE ALERT TO FILES
82  **/
83 
84 int
putalert(char * parent,char * name,FALERT * alertp)85 putalert(char *parent, char *name, FALERT *alertp)
86 {
87 	char			*path,
88 				cur_dir[PATH_MAX + 1],
89 				buf[BUFSIZ];
90 
91 	int			cur_umask;
92 
93 	int fdout, fdin;
94 
95 
96 	if (!parent || !*parent || !name || !*name) {
97 		errno = EINVAL;
98 		return (-1);
99 	}
100 
101 	if (!alertp->shcmd) {
102 		errno = EINVAL;
103 		return (-1);
104 	}
105 
106 	if (STREQU(alertp->shcmd, NAME_NONE))
107 		return (delalert(parent, name));
108 
109 	/*
110 	 * See if the form/printer/print-wheel exists.
111 	 */
112 
113 	if (!(path = makepath(parent, name, (char *)0)))
114 		return (-1);
115 
116 	if (Access(path, F_OK) == -1) {
117 		if (errno == ENOENT)
118 			errno = ENOTDIR; /* not quite, but what else? */
119 		Free (path);
120 		return (-1);
121 	}
122 	Free (path);
123 
124 	/*
125 	 * First, the shell command file.
126 	 */
127 
128 	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
129 		return (-1);
130 
131 	if ((fdout = open_locked(path, "w", MODE_NOEXEC)) < 0) {
132 		Free (path);
133 		return (-1);
134 	}
135 	Free (path);
136 
137 	/*
138 	 * We use a prototype file to build the shell command,
139 	 * so that the alerts are easily customized. The shell
140 	 * is expected to handle repeat alerts and failed alerts,
141 	 * because the Spooler doesn't. Also, the Spooler runs
142 	 * each alert with the UID and GID of the administrator
143 	 * who defined the alert. Otherwise, anything goes.
144 	 */
145 
146 	if (!Lp_Bin) {
147 		getpaths ();
148 		if (!Lp_Bin)
149 			return (-1);
150 	}
151 	if (!(path = makepath(Lp_Bin, ALERTPROTOFILE, (char *)0)))
152 		return (-1);
153 
154 	if ((fdin = open_locked(path, "r", 0)) < 0) {
155 		Free (path);
156 		return (-1);
157 	}
158 	Free (path);
159 
160 	errno = 0;
161 	while (fdgets(buf, BUFSIZ, fdin)) {
162 		int			key;
163 		char			*cp,
164 					*dash;
165 
166 		cp = buf;
167 		while ((dash = strchr(cp, '-'))) {
168 
169 		    *dash = 0;
170 		    fdputs (cp, fdout);
171 		    *(cp = dash) = '-';
172 
173 		    for (key = 0; key < NALRT_KEYS; key++)
174 			if (STRNEQU(
175 				cp,
176 				shell_keys[key].v,
177 				shell_keys[key].len
178 			)) {
179 				register char	*newline =
180 						(cp != buf)? "\n" : "";
181 
182 				cp += shell_keys[key].len;
183 
184 				switch (key) {
185 
186 				case ALRT_ENV:
187 					fdprintf(fdout, newline);
188 					envlist(fdout, environ);
189 					break;
190 
191 				case ALRT_PWD:
192 					getcwd (cur_dir, PATH_MAX);
193 					fdprintf (fdout, "%s", cur_dir);
194 					break;
195 
196 				case ALRT_ULIMIT:
197 					fdprintf (fdout, "%ld", ulimit(1, (long)0));
198 					break;
199 
200 				case ALRT_UMASK:
201 					umask (cur_umask = umask(0));
202 					fdprintf (fdout, "%03o", cur_umask);
203 					break;
204 
205 				case ALRT_INTERVAL:
206 					fdprintf(fdout, "%ld", (long)alertp->W);
207 					break;
208 
209 				case ALRT_CMD:
210 					fdprintf(fdout, newline);
211 					fdprintf(fdout, "%s\n", ALRT_CMDSTART);
212 					fdprintf(fdout, "%s\n", alertp->shcmd);
213 					fdprintf(fdout, "%s\n", ALRT_CMDEND);
214 					break;
215 
216 				case ALRT_USER:
217 					fdprintf(fdout, "%s", getname());
218 					break;
219 
220 				}
221 
222 				break;
223 			}
224 		    if (key >= NALRT_KEYS)
225 			fdputc(*cp++, fdout);
226 
227 		}
228 		fdputs(cp, fdout);
229 
230 	}
231 	if (errno != 0) {
232 		int			save_errno = errno;
233 
234 		close(fdin);
235 		close(fdout);
236 		errno = save_errno;
237 		return (-1);
238 	}
239 	close(fdin);
240 	close(fdout);
241 
242 	/*
243 	 * Next, the variables file.
244 	 */
245 
246 	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
247 		return (-1);
248 
249 	if ((fdout = open_locked(path, "w", MODE_NOREAD)) < 0) {
250 		Free (path);
251 		return (-1);
252 	}
253 	Free (path);
254 
255 	fdprintf(fdout, "%d\n", alertp->Q > 0? alertp->Q : 1);
256 	fdprintf(fdout, "%d\n", alertp->W >= 0? alertp->W : 0);
257 
258 	close(fdout);
259 
260 	return (0);
261 }
262 
263 /**
264  ** getalert() - EXTRACT ALERT FROM FILES
265  **/
266 
267 FALERT *
getalert(char * parent,char * name)268 getalert(char *parent, char *name)
269 {
270 	int fd;
271 	char *tmp;
272 	static FALERT		alert;
273 	register char		*path;
274 	char			buf[BUFSIZ];
275 	int			len;
276 
277 	if (!parent || !*parent || !name || !*name) {
278 		errno = EINVAL;
279 		return (0);
280 	}
281 
282 	/*
283 	 * See if the form/printer/print-wheel exists.
284 	 */
285 
286 	if (!(path = makepath(parent, name, (char *)0)))
287 		return (0);
288 
289 	if (Access(path, F_OK) == -1) {
290 		if (errno == ENOENT)
291 			errno = ENOTDIR; /* not quite, but what else? */
292 		Free (path);
293 		return (0);
294 	}
295 	Free (path);
296 
297 	/*
298 	 * First, the shell command file.
299 	 */
300 
301 	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
302 		return (0);
303 
304 	if ((fd = open_locked(path, "r", 0)) < 0) {
305 		Free (path);
306 		return (0);
307 	}
308 	Free (path);
309 
310 	/*
311 	 * Skip over environment setting stuff, while loop, etc.,
312 	 * to find the beginning of the command.
313 	 */
314 	errno = 0;
315 	while ((tmp =  fdgets(buf, BUFSIZ, fd)) &&
316 		!STRNEQU(buf, ALRT_CMDSTART, sizeof(ALRT_CMDSTART)-1))
317 		;
318 	if ((tmp == NULL) || (errno != 0)) {
319 		int			save_errno = errno;
320 
321 		close(fd);
322 		errno = save_errno;
323 		return (0);
324 	}
325 
326 	alert.shcmd = sop_up_rest(fd, ALRT_CMDEND);
327 
328 	close(fd);
329 
330 	if (!alert.shcmd)
331 		return (0);
332 
333 	/*
334 	 * Drop terminating newline.
335 	 */
336 	if (alert.shcmd[(len = strlen(alert.shcmd)) - 1] == '\n')
337 		alert.shcmd[len - 1] = 0;
338 
339 
340 	/*
341 	 * Next, the variables file.
342 	 */
343 
344 	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
345 		return (0);
346 
347 	if ((fd = open_locked(path, "r", 0)) < 0) {
348 		Free (path);
349 		return (0);
350 	}
351 	Free (path);
352 
353 	errno = 0;
354 	(void)fdgets (buf, BUFSIZ, fd);
355 	if (errno != 0) {
356 		int			save_errno = errno;
357 
358 		close(fd);
359 		errno = save_errno;
360 		return (0);
361 	}
362 	alert.Q = atoi(buf);
363 
364 	(void)fdgets (buf, BUFSIZ, fd);
365 	if (errno != 0) {
366 		int			save_errno = errno;
367 
368 		close(fd);
369 		errno = save_errno;
370 		return (0);
371 	}
372 	alert.W = atoi(buf);
373 
374 	close(fd);
375 
376 	return (&alert);
377 }
378 
379 /**
380  ** delalert() - DELETE ALERT FILES
381  **/
382 
383 int
delalert(char * parent,char * name)384 delalert(char *parent, char *name)
385 {
386 	char			*path;
387 
388 
389 	if (!parent || !*parent || !name || !*name) {
390 		errno = EINVAL;
391 		return (-1);
392 	}
393 
394 	/*
395 	 * See if the form/printer/print-wheel exists.
396 	 */
397 
398 	if (!(path = makepath(parent, name, (char *)0)))
399 		return (-1);
400 
401 	if (Access(path, F_OK) == -1) {
402 		if (errno == ENOENT)
403 			errno = ENOTDIR; /* not quite, but what else? */
404 		Free (path);
405 		return (-1);
406 	}
407 	Free (path);
408 
409 	/*
410 	 * Remove the two files.
411 	 */
412 
413 	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
414 		return (-1);
415 	if (rmfile(path) == -1) {
416 		Free (path);
417 		return (-1);
418 	}
419 	Free (path);
420 
421 	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
422 		return (-1);
423 	if (rmfile(path) == -1) {
424 		Free (path);
425 		return (-1);
426 	}
427 	Free (path);
428 
429 	return (0);
430 }
431 
432 /**
433  ** envlist() - PRINT OUT ENVIRONMENT LIST SAFELY
434  **/
435 
436 static void
envlist(int fd,char ** list)437 envlist(int fd, char **list)
438 {
439 	register char		*env,
440 				*value;
441 
442 	if (!list || !*list)
443 		return;
444 
445 	while ((env = *list++)) {
446 		if (!(value = strchr(env, '=')))
447 			continue;
448 		*value++ = 0;
449 		if (!strchr(value, '\''))
450 			fdprintf(fd, (char *)gettext("export %s; %s='%s'\n"),
451 				env, env, value);
452 		*--value = '=';
453 	}
454 }
455 
456 /*
457  * printalert() - PRINT ALERT DESCRIPTION
458  *
459  * This is not used in the scheduler, so we don't need to switch to using
460  * file descriptors for scalability.
461  */
462 
463 void
printalert(FILE * fp,FALERT * alertp,int isfault)464 printalert(FILE *fp, FALERT *alertp, int isfault)
465 {
466 	if (!alertp->shcmd) {
467 		if (isfault)
468 			(void)fprintf (fp, (char *)gettext("On fault: no alert\n"));
469 		else
470 			(void)fprintf (fp, (char *)gettext("No alert\n"));
471 
472 	} else {
473 		register char	*copy = Strdup(alertp->shcmd),
474 				*cp;
475 
476 		if (isfault)
477 			(void)fprintf (fp, (char *)gettext("On fault: "));
478 		else
479 			if (alertp->Q > 1)
480 				(void)fprintf (
481 					fp,
482 					(char *)gettext("When %d are queued: "),
483 					alertp->Q
484 				);
485 			else
486 				(void)fprintf (fp, (char *)gettext("Upon any being queued: "));
487 
488 		if (copy && (cp = strchr(copy, ' ')))
489 			while (*cp == ' ')
490 				*cp++ = 0;
491 
492 		if (
493 			copy
494 		     && syn_name(cp)
495 		     && (
496 				STREQU(copy, NAME_WRITE)
497 			     || STREQU(copy, NAME_MAIL)
498 			)
499 		)
500 			(void)fprintf (fp, "%s to %s ", copy, cp);
501 		else
502 			(void)fprintf (fp, (char *)gettext("alert with \"%s\" "), alertp->shcmd);
503 
504 		if (alertp->W > 0)
505 			(void)fprintf (fp, (char *)gettext("every %d minutes\n"), alertp->W);
506 		else
507 			(void)fprintf (fp, (char *)gettext("once\n"));
508 
509 		Free (copy);
510 	}
511 	return;
512 }
513