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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22/*	  All Rights Reserved  	*/
23
24/*
25 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26 * Use is subject to license terms.
27 */
28
29#include "uucp.h"
30#include <grp.h>
31
32#define G_EXT	0
33#define	G_INT	1
34#define	G_RES	2
35#define	G_ACT	3
36#define	G_IDF	4
37#define	G_MAX	512	/* max number of fields in the Grades file line */
38#define	SMBUF	128
39
40#define	TYPE	0
41#define FILE1	1
42#define	FILE2	2
43#define	USER	3
44#define	OPTS	4
45#define	FILE3	5
46
47extern int rdfulline(), jsize(), gdirf(), gnamef();
48extern void wfcommit();
49
50static void	mailAdmin();		/* Send mail to administrator. */
51
52/*
53 * chkgrp - checks to see the group has permission
54 *		to use a service grade queue.
55 *
56 * returns
57 *
58 *	SUCCESS - if the group has permissions
59 *	FAIL - if group does not
60 *
61 */
62
63static int
64chkgrp(carray,na)
65char **carray;
66int na;
67{
68	struct group *grp;
69	int i;
70	gid_t gid;
71
72	gid = getgid();
73	grp = getgrgid(gid);
74
75	for (i = G_IDF; i < na; i++)
76			if (EQUALS(carray[i], grp->gr_name))
77				return(SUCCESS);
78
79	return(FAIL);
80}
81
82/*
83 * chkusr - checks the permission fields of the Grades file
84 *	    to determine if the user can queue to a particular service grade.
85 *
86 * returns
87 *
88 *	SUCCESS - if the user can queue to the service grade.
89 *	FAIL - if the user can not queue to this service grade.
90 *
91 */
92
93static int
94chkusr(carray, na)
95char **carray;
96int na;
97{
98	int i;
99
100	/*
101	 * start at the point where the users are supposed to be in the
102	 * Grades file. Loop thru until the end of the user list is
103	 * found or the user name is found. If the user name is found then
104	 * return TRUE. If the end of the list is found, return FAIL.
105	 */
106
107	DEBUG(9, "User (%s)\n", User);
108
109	/* check for any user and return if so */
110
111	if (EQUALS(carray[G_IDF], "Any"))
112		return(SUCCESS);
113
114	DEBUG(9, "Members of administrator defined service grade (%s)\n", carray[G_EXT]);
115
116	for (i = G_IDF; i < na; i++) {
117		DEBUG(9, "%s\n", carray[i]);
118		if (EQUALS(User, carray[i]))
119			return(SUCCESS);
120	}
121
122	return(FAIL);
123}
124
125/*
126 *	fgrade - finds the appropiate queue to queue a job into
127 *
128 *	returns
129 *		SUCCESS	-> found a queue
130 *		FAIL	-> can't find a queue
131 */
132
133int
134fgrade(scfile)
135struct cs_struct *scfile;
136{
137	char fdgrade();
138	FILE *cfd;
139	char line[BUFSIZ];
140	char *carray[G_MAX];
141	long climit;
142
143	/* Check for the default service grade first */
144
145	if (strcmp(scfile->sgrade, "default") == 0) {
146		scfile->grade = fdgrade();
147		return(SUCCESS);
148	}
149
150	/* open grades file to begin a linear for the grade requested */
151
152	cfd = fopen(GRADES, "r");
153
154	/* loop until the file is empty or we find the grade we want */
155
156	while (rdfulline(cfd, line, BUFSIZ) != 0) {
157		(void) getargs(line, carray, G_MAX);
158
159		/* check to see if this is the grade we want */
160
161		if (!EQUALS(scfile->sgrade, carray[G_EXT]))
162			continue;
163
164		if (jsize(scfile, carray[G_RES], &climit) != FAIL) {
165			(void) fclose(cfd);
166			scfile->grade = *carray[G_INT];
167			return(SUCCESS);
168		}
169	}
170
171	(void) fclose(cfd);
172
173	(void) fprintf(stderr, gettext("Job size (%ld bytes)"
174	    " exceeds maximum number of bytes (%ld bytes)"
175	    " allowed into this service grade (%s).\n"
176	    "Job queued to default grade.\n"),
177	    scfile->jsize, climit, scfile->sgrade);
178
179	scfile->grade = fdgrade();
180	return(SUCCESS);
181}
182
183/*
184 *	fdgrade - finds the default queue for this system
185 *
186 *	returns
187 *		a one char name for the default queue
188 *
189 */
190
191char
192fdgrade()
193{
194	FILE *cfd;
195	char line[BUFSIZ];
196	char *carray[G_MAX];
197
198	/* Check for the default grade first */
199
200		cfd = fopen(GRADES, "r");
201
202		/* loop until the end of the file is read */
203
204		for (; rdfulline(cfd, line, BUFSIZ) != 0;) {
205
206			/* parse the fields of this line */
207
208			(void) getargs(line, carray, G_MAX);
209
210			/* check to see if the administrator has defined
211			 * a default grade for the machine.
212			 */
213
214			if (strcmp(carray[G_EXT], "default") != 0)
215				continue;
216
217			/* default must be defined in the file
218			 *  close the file, get the queue name, and return.
219			 */
220
221			(void) fclose(cfd);
222			return(*carray[G_INT]);
223		}
224
225		/* no default defined in this file. close file.
226		 * get our default queue and return.
227		 */
228
229		(void) fclose(cfd);
230		return(D_QUEUE);
231}
232
233/*
234 * job_size - determines the size of a job
235 *
236 * returns
237 *
238 *	SUCCESS - if the size of the job can be determined
239 *	FAIL	- otherwise
240 */
241
242int
243job_size(scfile)
244struct cs_struct *scfile;
245{
246	extern int Dfileused;
247	struct stat s;
248	FILE *fp;
249	char line[BUFSIZ];
250	char *carray[G_MAX];
251	int na;
252	int nodfile = FALSE;
253	int ret;
254
255	scfile->jsize = 0;
256
257	fp = fopen(scfile->file, "r");
258
259	if (fp == NULL) {
260		toCorrupt(scfile->file);
261		errent(Ct_OPEN, scfile->file, errno, __FILE__,  __LINE__);
262	}
263
264	while (fgets(line, BUFSIZ, fp) != NULL) {
265		na = getargs(line, carray, G_MAX);
266
267		if (na < 6) {
268			(void) fclose(fp);
269			toCorrupt(scfile->file);
270			errent("BAD NUMBER OF ARGUMENTS", scfile->file, 0,
271				__FILE__, __LINE__);
272		}
273
274		/* if the type of a transfer is not a push
275		 * then don't try to determine the size of
276		 * the data file, because you can't.
277		 */
278
279		if (*carray[TYPE] == 'R')
280			continue;
281
282		/* find the data dile that is to be transferred */
283
284		if ((ret = stat(carray[FILE3], &s)) != 0) {
285			if (errno == ENOENT) {
286				nodfile = TRUE;
287				ret = stat(carray[FILE1], &s);
288			}
289		}
290		else
291			Dfileused = TRUE;
292
293		/*
294		 * check to see if the return code from stat was 0
295		 * if return code was not 0, write message to error
296		 * log and quit. Otherwise, add size of file to job
297		 * size and continue looping.
298		 */
299
300		if (ret != 0) {
301			(void) fclose(fp);
302			errent(Ct_STAT, nodfile ?
303				carray[FILE1] : carray[FILE3], errno,
304				__FILE__, __LINE__);
305		}
306
307		nodfile = FALSE;
308		scfile->jsize += s.st_size;
309	}
310	(void) fclose(fp);
311	return(SUCCESS);
312}
313
314static void lcase();
315
316/*
317 * jsize - determines whether if a job is small enough to
318 * 	   be placed in the appropiate queue.
319 *
320 * returns
321 *
322 *	SUCCESS - if the size of the job is less than or
323 *		  equal to the number of bytes in the restriction
324 *		  of the GRADES file.
325 *
326 *	FAIL	- otherwise
327 */
328
329int
330jsize(scfile, climit, nlimit)
331struct cs_struct *scfile;
332char *climit;
333long *nlimit;
334{
335#define ONE_K (1024)
336#define ONE_MEG ((1024)*(1024))
337
338	char rest[SMBUF];
339	char msg[BUFSIZ], *p;
340
341	if (EQUALS(climit, "Any"))
342		return(SUCCESS);
343
344	lcase(climit, rest, SMBUF);
345
346	if (!(p = strchr(rest, 'k')) && (!(p = strchr(rest, 'm')))) {
347
348		for(p = climit; *p; ++p) {
349			if (isdigit(*p))
350				continue;
351
352			/* corrupt restriction field in the Grades file.
353			 * report it to the uucp administrator.
354			 */
355
356			snprintf(msg, sizeof (msg),
357			    gettext("Error encountered in the"
358			    " restrictions field of the Grades file."
359			    "  Field contents (%s)."), climit);
360			mailAdmin(msg);
361			return(SUCCESS);
362		}
363
364		*nlimit = atol(climit);
365	}
366	else if (*p == 'k') {
367		*p = '\0';
368		*nlimit = (long) (atof(rest) * ONE_K);
369	}
370	else {
371		*p = '\0';
372		*nlimit = (long) (atof(rest) * ONE_MEG);
373	}
374
375	if (scfile->jsize <= *nlimit)
376		return(SUCCESS);
377	else
378		return(FAIL);
379}
380
381static void
382lcase(s, t, lim)
383char s[], t[];
384int lim;
385{
386	char *p;
387	int i;
388
389
390	p = s;
391
392	for (i = 0; i < lim-1 && *p; i++)
393		if (isupper(*p))
394			t[i] = tolower(*p++);
395		else
396			t[i] = *p++;
397
398	t[i] = '\0';
399	return;
400}
401
402/*
403 * mailAdmin - mail a message to the uucp administrator.
404 *
405 * returns:
406 *
407 *	nothing
408 */
409
410static void
411mailAdmin (msg)
412
413char *	msg;
414
415{
416	char	cmd[BUFSIZ];		/* Place to build mail command. */
417	FILE *	mail;			/* Channel to write mail on. */
418
419	(void) sprintf(cmd, "%s %s %s", PATH, MAIL, "uucp");
420	if ((mail = popen(cmd, "w")) != (FILE *) NULL)
421	{
422		(void) fprintf(mail, "To: uucp\nSubject: %s\n\n%s\n",
423		    gettext("Grades file problem"), msg);
424		(void) pclose(mail);
425	}
426
427	/*
428	 * Ignore popen failure.  There is not much that we can do if
429	 * it fails, since we are already trying to notify the administrator
430	 * of a problem.
431	 */
432	return;
433}
434
435/*
436 * putdfiles - moves any and all of the D. to the spool directory for
437 * 	       a C. file.
438 *
439 * returns
440 *
441 *	nothing
442 */
443
444void
445putdfiles(scfile)
446struct cs_struct scfile;
447{
448	FILE *fp;
449	char line[BUFSIZ];
450	char *carray[G_MAX];
451	int na;
452	struct stat s;
453
454	fp = fopen(scfile.file, "r");
455
456	if (fp == NULL) {
457		toCorrupt(scfile.file);
458		errent(Ct_OPEN, scfile.file, errno, __FILE__, __LINE__);
459	}
460
461	while (fgets(line, BUFSIZ, fp) != NULL) {
462
463		na = getargs(line, carray, G_MAX);
464		if (na < 6) {
465			(void) fclose(fp);
466			toCorrupt(scfile.file);
467			errent("BAD NUMBER OF ARGUMENTS", scfile.file, 0,
468				__FILE__, __LINE__);
469		}
470
471		if (*carray[TYPE] == 'R')
472			continue;
473
474	    	/* move D. file to the spool area */
475
476		if (stat(carray[FILE3], &s) != -1)
477			wfcommit(carray[FILE3], carray[FILE3], scfile.sys);
478	}
479
480	(void) fclose(fp);
481	return;
482}
483
484/*
485 * reads a line from a file and takes care of comment lines
486 * and continuations (\) in last column.
487 *
488 * return:
489 *	the number of chars that are placed in line.
490 */
491
492int
493rdfulline(fd, line, lim)
494FILE *fd;
495char *line;
496int lim;
497{
498	register char *p, *c;
499	char buf[BUFSIZ];
500	size_t blr, btox;
501
502	p = line;
503	for (;fgets(buf, BUFSIZ, fd) != NULL;) {
504		/* check to see if it is a comment */
505
506		if (buf[0] == '#')
507			continue;
508
509		/* remove trailing white space */
510		c = &buf[strlen(buf)-1];
511		while (c>=buf && (*c == '\n' || *c == '\t' || *c == ' ') )
512			*c-- = NULLCHAR;
513
514		if (buf[0] == '\n' || buf[0] == NULLCHAR)
515			continue;
516
517		blr = lim - 1 - (p - line);
518		btox = blr < strlen(buf) ? blr : strlen(buf);
519
520		if (btox <= 0)
521			break;
522
523		(void) strncpy(p, buf, btox);
524		p += btox - 1;
525
526		if ( *(p-1) == '\\')
527			p--;
528		else
529			break;
530	}
531
532	*++p = '\0';
533	return(p-line-1);
534}
535
536/*	upermit - checks to determine if the user has permissions
537 *	to use administrator defined service grade.
538 *
539 *	returns
540 *		SUCCESS -> if the user can queue to this service grade.
541 *		FAIL -> if the user cannot queue to this service grade.
542 */
543
544int
545upermit(carray, na)
546char **carray;
547int na;
548{
549#define G_USR "user"
550#define G_NUSR "non-user"
551#define G_GRP "group"
552#define G_NGRP "non-group"
553
554	char actn[SMBUF];
555	char ufld[SMBUF];
556	char msg[BUFSIZ];
557
558	(void) strcpy(actn, carray[G_ACT]);
559
560	lcase(actn, ufld, SMBUF);
561
562	if (EQUALS(ufld, G_USR))
563		return(chkusr(carray,na));
564
565	if (EQUALS(ufld, G_NUSR))
566		return((chkusr(carray, na) != SUCCESS) ? SUCCESS : FAIL);
567
568	if (EQUALS(ufld, G_GRP))
569		return(chkgrp(carray, na));
570
571	if (EQUALS(ufld, G_NGRP))
572		return((chkgrp(carray, na) != SUCCESS) ? SUCCESS : FAIL);
573
574	(void) snprintf(msg, sizeof (msg),
575	    gettext("Error encountered in action field of"
576	    " the Grades file. Field contents (%s)."), carray[G_ACT]);
577	mailAdmin(msg);
578	return(FAIL);
579}
580
581/*
582 *	vergrd - verify if the grade name is a valid administrator
583 *		 defined service grade name and if the user has the
584 *		 appropiate permission to use this grade.
585 *
586 *	returns
587 *		SUCCESS	-> grade is valid and user is
588 *			   permitted to use this grade.
589 *		FAIL	-> otherwise
590 *
591 */
592
593int
594vergrd(grade)
595char *grade;
596{
597	FILE *cfd;
598	char line[BUFSIZ];
599	char *carray[G_MAX];
600	int na;
601
602	/* Check for the default grade first */
603
604	if (EQUALS(grade, "default"))
605		return(SUCCESS);
606
607	/* open grades file to begin a linear for the grade requested */
608
609	cfd = fopen(GRADES, "r");
610
611	/* loop until the file is empty or we find the grade we want */
612
613	while (rdfulline(cfd, line, BUFSIZ) != 0) {
614		na = getargs(line, carray, G_MAX);
615
616		/* check to see if this is the grade we want */
617
618		if (!EQUALS(grade, carray[G_EXT]))
619			continue;
620
621		/* check for the permission on this grade */
622
623		if (upermit(carray, na) != FAIL) {
624			(void) fclose(cfd);
625			return(SUCCESS);
626		}
627		else {
628			(void) fclose(cfd);
629			(void) fprintf(stderr, gettext("User does not have"
630			    " permission to use this service grade (%s).\n"
631			    "Job has not been queued.\n"
632			    "Use (uuglist) to find which service grades"
633			    " you can queue to.\n"), grade);
634			return(FAIL);
635		}
636	}
637
638	(void) fclose(cfd);
639
640	(void) fprintf(stderr, gettext(
641	    "Service grade (%s) does not exist on this machine."
642	    "  Job not queued.\n"
643	    "Use (uuglist) to find which service grades are available on"
644	    " this machine.\n"), grade);
645	return(FAIL);
646}
647
648/*
649 * wfremove - removes a C. file from the Workspace directory and all of its
650 * D. files.
651 */
652
653void
654wfremove(file)
655char *file;
656{
657	FILE *fp;
658	char line[BUFSIZ];
659	char *carray[G_MAX];
660	int na;
661	struct stat s;
662
663	fp = fopen(file, "r");
664
665	if (fp == NULL) {
666		toCorrupt(file);
667		errent(Ct_OPEN, file, errno, __FILE__, __LINE__);
668	}
669
670	while (fgets(line, BUFSIZ, fp) != NULL) {
671		na = getargs(line, carray, G_MAX);
672
673		if (na < 6) {
674			(void) fclose(fp);
675			toCorrupt(file);
676			errent("BAD NUMBER OF ARGUMENTS", file, 0,
677				__FILE__, __LINE__);
678		}
679
680		if (*carray[TYPE] == 'R')
681			continue;
682
683	    	/* remove D. file */
684
685	    	DEBUG(4, "Removing data file (%s)\n", carray[FILE3]);
686
687		if ((stat(carray[FILE3], &s) != -1) && (unlink(carray[FILE3]) != 0)) {
688			(void) fclose(fp);
689			toCorrupt(file);
690			toCorrupt(carray[FILE3]);
691			errent(Ct_UNLINK, carray[FILE3], errno, __FILE__,
692				__LINE__);
693		}
694	}
695
696	(void) fclose(fp);
697
698	DEBUG(4, "Removing work file (%s)\n", file);
699
700	if (unlink(file) != 0) {
701		toCorrupt(file);
702		errent(Ct_UNLINK, file, errno, __FILE__, __LINE__);
703	}
704	return;
705}
706
707/*
708 * findgrade - finds the highest priority job grade that is not locked
709 * and that has jobs.
710 *
711 * job grade name is null, if no job grade is found.
712 */
713
714void
715findgrade(dir, jobgrade)
716char *dir, *jobgrade;
717{
718	char prevgrade[MAXBASENAME+1], curgrade[MAXBASENAME+1],
719	     gradedir[MAXBASENAME+1];
720	char lockname[MAXFULLNAME];
721	char Cfile[MAXBASENAME+1];
722	DIR *p, *q;
723
724	*prevgrade = NULLCHAR;
725	p = opendir(dir);
726	ASSERT(p != NULL, Ct_OPEN, dir, errno);
727
728	while (gdirf(p, gradedir, dir) == TRUE) {
729		(void) sprintf(lockname, "%s.%.*s.%s", LOCKPRE, SYSNSIZE,
730		    Rmtname, gradedir);
731		if (cklock(lockname) == FAIL)
732			continue;
733		q = opendir(gradedir);
734		ASSERT(q != NULL, Ct_OPEN, gradedir, errno);
735		while (gnamef(q, Cfile) == TRUE) {
736			if (Cfile[0] == CMDPRE) {
737				if (*prevgrade == NULLCHAR) {
738					(void) strcpy(prevgrade, gradedir);
739					break;
740				}
741				(void) strcpy(curgrade, gradedir);
742				if (strcmp(curgrade, prevgrade) < 0)
743					(void) strcpy(prevgrade, curgrade);
744			}
745		}
746		closedir(q);
747	}
748	closedir(p);
749	(void) strncpy(jobgrade, prevgrade, MAXBASENAME);
750	jobgrade[MAXBASENAME] = NULLCHAR;
751	return;
752}
753