1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * trap  [-p]  action sig...
23 * kill  [-l] [sig...]
24 * kill  [-s sig] pid...
25 *
26 *   David Korn
27 *   AT&T Labs
28 *   research!dgk
29 *
30 */
31
32#include	"defs.h"
33#include	"jobs.h"
34#include	"builtins.h"
35
36#define L_FLAG	1
37#define S_FLAG	2
38
39static const char trapfmt[] = "trap -- %s %s\n";
40
41static int	sig_number(Shell_t*,const char*);
42static void	sig_list(Shell_t*,int);
43
44int	b_trap(int argc,char *argv[],Shbltin_t *context)
45{
46	register char *arg = argv[1];
47	register int sig, clear = 0, dflag = 0, pflag = 0;
48	register Shell_t *shp = context->shp;
49	NOT_USED(argc);
50	while (sig = optget(argv, sh_opttrap)) switch (sig)
51	{
52	    case 'p':
53		pflag=1;
54		break;
55	    case ':':
56		errormsg(SH_DICT,2, "%s", opt_info.arg);
57		break;
58	    case '?':
59		errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
60		return(2);
61		break;
62	}
63	argv += opt_info.index;
64	if(error_info.errors)
65		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0));
66	if(arg = *argv)
67	{
68		char *action = arg;
69		if(!dflag && !pflag)
70		{
71			/* first argument all digits or - means clear */
72			while(isdigit(*arg))
73				arg++;
74			clear = (arg!=action && *arg==0);
75			if(!clear)
76			{
77				++argv;
78				if(*action=='-' && action[1]==0)
79					clear++;
80				/*
81				 * NOTE: 2007-11-26: workaround for tests/signal.sh
82				 * if function semantics can be worked out then it
83				 * may merit a -d,--default option
84				 */
85				else if(*action=='+' && action[1]==0 && shp->st.self == &shp->global)
86				{
87					clear++;
88					dflag++;
89				}
90			}
91			if(!argv[0])
92				errormsg(SH_DICT,ERROR_exit(1),e_condition);
93		}
94		while(arg = *argv++)
95		{
96			sig = sig_number(shp,arg);
97			if(sig<0)
98			{
99				errormsg(SH_DICT,2,e_trap,arg);
100				return(1);
101			}
102			/* internal traps */
103			if(sig&SH_TRAP)
104			{
105				char **trap = (shp->st.otrap?shp->st.otrap:shp->st.trap);
106				sig &= ~SH_TRAP;
107				if(sig>SH_DEBUGTRAP)
108				{
109					errormsg(SH_DICT,2,e_trap,arg);
110					return(1);
111				}
112				if(pflag)
113				{
114					if(arg=trap[sig])
115						sfputr(sfstdout,sh_fmtq(arg),'\n');
116					continue;
117				}
118				shp->st.otrap = 0;
119				if(shp->st.trap[sig])
120					free(shp->st.trap[sig]);
121				shp->st.trap[sig] = 0;
122				if(!clear && *action)
123					shp->st.trap[sig] = strdup(action);
124				if(sig == SH_DEBUGTRAP)
125				{
126					if(shp->st.trap[sig])
127						shp->trapnote |= SH_SIGTRAP;
128					else
129						shp->trapnote = 0;
130				}
131				continue;
132			}
133			if(sig>shp->gd->sigmax)
134			{
135				errormsg(SH_DICT,2,e_trap,arg);
136				return(1);
137			}
138			else if(pflag)
139			{
140				char **trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom);
141				if(arg=trapcom[sig])
142					sfputr(sfstdout,arg,'\n');
143			}
144			else if(clear)
145			{
146				sh_sigclear(sig);
147				if(dflag)
148					signal(sig,SIG_DFL);
149			}
150			else
151			{
152				if(sig >= shp->st.trapmax)
153					shp->st.trapmax = sig+1;
154				arg = shp->st.trapcom[sig];
155				sh_sigtrap(sig);
156				shp->st.trapcom[sig] = (shp->sigflag[sig]&SH_SIGOFF) ? Empty : strdup(action);
157				if(arg && arg != Empty)
158					free(arg);
159			}
160		}
161	}
162	else /* print out current traps */
163		sig_list(shp,-2);
164	return(0);
165}
166
167int	b_kill(int argc,char *argv[],Shbltin_t *context)
168{
169	register char *signame;
170	register int sig=SIGTERM, flag=0, n;
171	register Shell_t *shp = context->shp;
172	int usemenu = 0;
173	NOT_USED(argc);
174	while((n = optget(argv,sh_optkill))) switch(n)
175	{
176		case ':':
177			if((signame=argv[opt_info.index++]) && (sig=sig_number(shp,signame+1))>=0)
178				goto endopts;
179			opt_info.index--;
180			errormsg(SH_DICT,2, "%s", opt_info.arg);
181			break;
182		case 'n':
183			sig = (int)opt_info.num;
184			goto endopts;
185		case 's':
186			flag |= S_FLAG;
187			signame = opt_info.arg;
188			goto endopts;
189		case 'L':
190			usemenu = -1;
191		case 'l':
192			flag |= L_FLAG;
193			break;
194		case '?':
195			errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
196			break;
197	}
198endopts:
199	argv += opt_info.index;
200	if(*argv && strcmp(*argv,"--")==0 && strcmp(*(argv-1),"--")!=0)
201		argv++;
202	if(error_info.errors || flag==(L_FLAG|S_FLAG) || (!(*argv) && !(flag&L_FLAG)))
203		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0));
204	/* just in case we send a kill -9 $$ */
205	sfsync(sfstderr);
206	if(flag&L_FLAG)
207	{
208		if(!(*argv))
209			sig_list(shp,usemenu);
210		else while(signame = *argv++)
211		{
212			if(isdigit(*signame))
213				sig_list(shp,((int)strtol(signame, (char**)0, 10)&0177)+1);
214			else
215			{
216				if((sig=sig_number(shp,signame))<0)
217				{
218					shp->exitval = 2;
219					errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame);
220				}
221				sfprintf(sfstdout,"%d\n",sig);
222			}
223		}
224		return(shp->exitval);
225	}
226	if(flag&S_FLAG)
227	{
228		if((sig=sig_number(shp,signame)) < 0 || sig > shp->gd->sigmax)
229			errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame);
230	}
231	if(job_walk(sfstdout,job_kill,sig,argv))
232		shp->exitval = 1;
233	return(shp->exitval);
234}
235
236/*
237 * Given the name or number of a signal return the signal number
238 */
239
240static int sig_number(Shell_t *shp,const char *string)
241{
242	const Shtable_t	*tp;
243	register int	n,o,sig=0;
244	char		*last, *name;
245	if(isdigit(*string))
246	{
247		n = strtol(string,&last,10);
248		if(*last)
249			n = -1;
250	}
251	else
252	{
253		register int c;
254		o = staktell();
255		do
256		{
257			c = *string++;
258			if(islower(c))
259				c = toupper(c);
260			stakputc(c);
261		}
262		while(c);
263		stakseek(o);
264		if(memcmp(stakptr(o),"SIG",3)==0)
265		{
266			sig = 1;
267			o += 3;
268			if(isdigit(*stakptr(o)))
269			{
270				n = strtol(stakptr(o),&last,10);
271				if(!*last)
272					return(n);
273			}
274		}
275		tp = sh_locate(stakptr(o),(const Shtable_t*)shtab_signals,sizeof(*shtab_signals));
276		n = tp->sh_number;
277		if(sig==1 && (n>=(SH_TRAP-1) && n < (1<<SH_SIGBITS)))
278		{
279			/* sig prefix cannot match internal traps */
280			n = 0;
281			tp = (Shtable_t*)((char*)tp + sizeof(*shtab_signals));
282			if(strcmp(stakptr(o),tp->sh_name)==0)
283				n = tp->sh_number;
284		}
285		if((n>>SH_SIGBITS)&SH_SIGRUNTIME)
286			n = shp->gd->sigruntime[(n&((1<<SH_SIGBITS)-1))-1];
287		else
288		{
289			n &= (1<<SH_SIGBITS)-1;
290			if(n < SH_TRAP)
291				n--;
292		}
293		if(n<0 && shp->gd->sigruntime[1] && (name=stakptr(o)) && *name++=='R' && *name++=='T')
294		{
295			if(name[0]=='M' && name[1]=='I' && name[2]=='N' && name[3]=='+')
296			{
297				if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name)
298					n = shp->gd->sigruntime[SH_SIGRTMIN] + sig;
299			}
300			else if(name[0]=='M' && name[1]=='A' && name[2]=='X' && name[3]=='-')
301			{
302				if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name)
303					n = shp->gd->sigruntime[SH_SIGRTMAX] - sig;
304			}
305			else if((sig=(int)strtol(name,&name,10)) > 0 && !*name)
306				n = shp->gd->sigruntime[SH_SIGRTMIN] + sig - 1;
307			if(n<shp->gd->sigruntime[SH_SIGRTMIN] || n>shp->gd->sigruntime[SH_SIGRTMAX])
308				n = -1;
309		}
310	}
311	return(n);
312}
313
314/*
315 * synthesize signal name for sig in buf
316 * pfx!=0 prepends SIG to default signal number
317 */
318static char* sig_name(Shell_t *shp,int sig, char* buf, int pfx)
319{
320	register int	i;
321
322	i = 0;
323	if(sig>shp->gd->sigruntime[SH_SIGRTMIN] && sig<shp->gd->sigruntime[SH_SIGRTMAX])
324	{
325		buf[i++] = 'R';
326		buf[i++] = 'T';
327		buf[i++] = 'M';
328		if(sig>shp->gd->sigruntime[SH_SIGRTMIN]+(shp->gd->sigruntime[SH_SIGRTMAX]-shp->gd->sigruntime[SH_SIGRTMIN])/2)
329		{
330			buf[i++] = 'A';
331			buf[i++] = 'X';
332			buf[i++] = '-';
333			sig = shp->gd->sigruntime[SH_SIGRTMAX]-sig;
334		}
335		else
336		{
337			buf[i++] = 'I';
338			buf[i++] = 'N';
339			buf[i++] = '+';
340			sig = sig-shp->gd->sigruntime[SH_SIGRTMIN];
341		}
342	}
343	else if(pfx)
344	{
345		buf[i++] = 'S';
346		buf[i++] = 'I';
347		buf[i++] = 'G';
348	}
349	i += sfsprintf(buf+i, 8, "%d", sig);
350	buf[i] = 0;
351	return buf;
352}
353
354/*
355 * if <flag> is positive, then print signal name corresponding to <flag>
356 * if <flag> is zero, then print all signal names
357 * if <flag> is -1, then print all signal names in menu format
358 * if <flag> is <-1, then print all traps
359 */
360static void sig_list(register Shell_t *shp,register int flag)
361{
362	register const struct shtable2	*tp;
363	register int sig;
364	register char *sname;
365	char name[10];
366	const char *names[SH_TRAP];
367	const char *traps[SH_DEBUGTRAP+1];
368	tp=shtab_signals;
369	if(flag<=0)
370	{
371		/* not all signals may be defined, so initialize */
372		for(sig=shp->gd->sigmax; sig>=0; sig--)
373			names[sig] = 0;
374		for(sig=SH_DEBUGTRAP; sig>=0; sig--)
375			traps[sig] = 0;
376	}
377	for(; *tp->sh_name; tp++)
378	{
379		sig = tp->sh_number&((1<<SH_SIGBITS)-1);
380		if (((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) && (sig = shp->gd->sigruntime[sig-1]+1) == 1)
381			continue;
382		if(sig==flag)
383		{
384			sfprintf(sfstdout,"%s\n",tp->sh_name);
385			return;
386		}
387		else if(sig&SH_TRAP)
388			traps[sig&~SH_TRAP] = (char*)tp->sh_name;
389		else if(sig-- && sig < elementsof(names))
390			names[sig] = (char*)tp->sh_name;
391	}
392	if(flag > 0)
393		sfputr(sfstdout, sig_name(shp,flag-1,name,0), '\n');
394	else if(flag<-1)
395	{
396		/* print the traps */
397		register char *trap,**trapcom;
398		sig = shp->st.trapmax;
399		/* use parent traps if otrapcom is set (for $(trap)  */
400		trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom);
401		while(--sig >= 0)
402		{
403			if(!(trap=trapcom[sig]))
404				continue;
405			if(sig > shp->gd->sigmax || !(sname=(char*)names[sig]))
406				sname = sig_name(shp,sig,name,1);
407			sfprintf(sfstdout,trapfmt,sh_fmtq(trap),sname);
408		}
409		for(sig=SH_DEBUGTRAP; sig>=0; sig--)
410		{
411			if(!(trap=shp->st.otrap?shp->st.otrap[sig]:shp->st.trap[sig]))
412				continue;
413			sfprintf(sfstdout,trapfmt,sh_fmtq(trap),traps[sig]);
414		}
415	}
416	else
417	{
418		/* print all the signal names */
419		for(sig=1; sig <= shp->gd->sigmax; sig++)
420		{
421			if(!(sname=(char*)names[sig]))
422			{
423				sname = sig_name(shp,sig,name,1);
424				if(flag)
425					sname = stakcopy(sname);
426			}
427			if(flag)
428				names[sig] = sname;
429			else
430				sfputr(sfstdout,sname,'\n');
431		}
432		if(flag)
433		{
434			names[sig] = 0;
435			sh_menu(sfstdout,shp->gd->sigmax,(char**)names+1);
436		}
437	}
438}
439