1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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(const char*);
42static void	sig_list(Shell_t*,int);
43
44int	b_trap(int argc,char *argv[],void *extra)
45{
46	register char *arg = argv[1];
47	register int sig, clear = 0, dflag = 0, pflag = 0;
48	register Shell_t *shp = ((Shbltin_t*)extra)->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 && sh.st.self == &sh.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(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				sig &= ~SH_TRAP;
106				if(sig>SH_DEBUGTRAP)
107				{
108					errormsg(SH_DICT,2,e_trap,arg);
109					return(1);
110				}
111				if(pflag)
112				{
113					if(arg=shp->st.trap[sig])
114						sfputr(sfstdout,sh_fmtq(arg),'\n');
115					continue;
116				}
117				if(shp->st.trap[sig])
118					free(shp->st.trap[sig]);
119				shp->st.trap[sig] = 0;
120				if(!clear && *action)
121					shp->st.trap[sig] = strdup(action);
122				if(sig == SH_DEBUGTRAP)
123				{
124					if(shp->st.trap[sig])
125						shp->trapnote |= SH_SIGTRAP;
126					else
127						shp->trapnote = 0;
128				}
129				continue;
130			}
131			if(sig>shp->sigmax)
132			{
133				errormsg(SH_DICT,2,e_trap,arg);
134				return(1);
135			}
136			else if(pflag)
137			{
138				char **trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom);
139				if(arg=trapcom[sig])
140					sfputr(sfstdout,arg,'\n');
141			}
142			else if(clear)
143			{
144				sh_sigclear(sig);
145				if(dflag)
146					signal(sig,SIG_DFL);
147			}
148			else
149			{
150				if(sig >= shp->st.trapmax)
151					shp->st.trapmax = sig+1;
152				arg = shp->st.trapcom[sig];
153				sh_sigtrap(sig);
154				shp->st.trapcom[sig] = (shp->sigflag[sig]&SH_SIGOFF) ? Empty : strdup(action);
155				if(arg && arg != Empty)
156					free(arg);
157			}
158		}
159	}
160	else /* print out current traps */
161		sig_list(shp,-1);
162	return(0);
163}
164
165int	b_kill(int argc,char *argv[],void *extra)
166{
167	register char *signame;
168	register int sig=SIGTERM, flag=0, n;
169	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
170	NOT_USED(argc);
171	while((n = optget(argv,sh_optkill))) switch(n)
172	{
173		case ':':
174			if((signame=argv[opt_info.index++]) && (sig=sig_number(signame+1))>=0)
175				goto endopts;
176			opt_info.index--;
177			errormsg(SH_DICT,2, "%s", opt_info.arg);
178			break;
179		case 'n':
180			sig = (int)opt_info.num;
181			goto endopts;
182		case 's':
183			flag |= S_FLAG;
184			signame = opt_info.arg;
185			goto endopts;
186		case 'l':
187			flag |= L_FLAG;
188			break;
189		case '?':
190			errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
191			break;
192	}
193endopts:
194	argv += opt_info.index;
195	if(*argv && strcmp(*argv,"--")==0 && strcmp(*(argv-1),"--")!=0)
196		argv++;
197	if(error_info.errors || flag==(L_FLAG|S_FLAG) || (!(*argv) && !(flag&L_FLAG)))
198		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0));
199	/* just in case we send a kill -9 $$ */
200	sfsync(sfstderr);
201	if(flag&L_FLAG)
202	{
203		if(!(*argv))
204			sig_list(shp,0);
205		else while(signame = *argv++)
206		{
207			if(isdigit(*signame))
208				sig_list(shp,((int)strtol(signame, (char**)0, 10)&0177)+1);
209			else
210			{
211				if((sig=sig_number(signame))<0)
212				{
213					shp->exitval = 2;
214					errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame);
215				}
216				sfprintf(sfstdout,"%d\n",sig);
217			}
218		}
219		return(shp->exitval);
220	}
221	if(flag&S_FLAG)
222	{
223		if((sig=sig_number(signame)) < 0 || sig > shp->sigmax)
224			errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame);
225	}
226	if(job_walk(sfstdout,job_kill,sig,argv))
227		shp->exitval = 1;
228	return(shp->exitval);
229}
230
231/*
232 * Given the name or number of a signal return the signal number
233 */
234
235static int sig_number(const char *string)
236{
237	const Shtable_t	*tp;
238	register int	n,o,sig=0;
239	char		*last, *name;
240	if(isdigit(*string))
241	{
242		n = strtol(string,&last,10);
243		if(*last)
244			n = -1;
245	}
246	else
247	{
248		register int c;
249		o = staktell();
250		do
251		{
252			c = *string++;
253			if(islower(c))
254				c = toupper(c);
255			stakputc(c);
256		}
257		while(c);
258		stakseek(o);
259		if(memcmp(stakptr(o),"SIG",3)==0)
260		{
261			sig = 1;
262			o += 3;
263			if(isdigit(*stakptr(o)))
264			{
265				n = strtol(stakptr(o),&last,10);
266				if(!*last)
267					return(n);
268			}
269		}
270		tp = sh_locate(stakptr(o),(const Shtable_t*)shtab_signals,sizeof(*shtab_signals));
271		n = tp->sh_number;
272		if(sig==1 && (n>=(SH_TRAP-1) && n < (1<<SH_SIGBITS)))
273		{
274			/* sig prefix cannot match internal traps */
275			n = 0;
276			tp = (Shtable_t*)((char*)tp + sizeof(*shtab_signals));
277			if(strcmp(stakptr(o),tp->sh_name)==0)
278				n = tp->sh_number;
279		}
280		if((n>>SH_SIGBITS)&SH_SIGRUNTIME)
281			n = sh.sigruntime[(n&((1<<SH_SIGBITS)-1))-1];
282		else
283		{
284			n &= (1<<SH_SIGBITS)-1;
285			if(n < SH_TRAP)
286				n--;
287		}
288		if(n<0 && sh.sigruntime[1] && (name=stakptr(o)) && *name++=='R' && *name++=='T')
289		{
290			if(name[0]=='M' && name[1]=='I' && name[2]=='N' && name[3]=='+')
291			{
292				if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name)
293					n = sh.sigruntime[SH_SIGRTMIN] + sig;
294			}
295			else if(name[0]=='M' && name[1]=='A' && name[2]=='X' && name[3]=='-')
296			{
297				if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name)
298					n = sh.sigruntime[SH_SIGRTMAX] - sig;
299			}
300			else if((sig=(int)strtol(name,&name,10)) > 0 && !*name)
301				n = sh.sigruntime[SH_SIGRTMIN] + sig - 1;
302			if(n<sh.sigruntime[SH_SIGRTMIN] || n>sh.sigruntime[SH_SIGRTMAX])
303				n = -1;
304		}
305	}
306	return(n);
307}
308
309/*
310 * synthesize signal name for sig in buf
311 * pfx!=0 prepends SIG to default signal number
312 */
313static char* sig_name(int sig, char* buf, int pfx)
314{
315	register int	i;
316
317	i = 0;
318	if(sig>sh.sigruntime[SH_SIGRTMIN] && sig<sh.sigruntime[SH_SIGRTMAX])
319	{
320		buf[i++] = 'R';
321		buf[i++] = 'T';
322		buf[i++] = 'M';
323		if(sig>sh.sigruntime[SH_SIGRTMIN]+(sh.sigruntime[SH_SIGRTMAX]-sh.sigruntime[SH_SIGRTMIN])/2)
324		{
325			buf[i++] = 'A';
326			buf[i++] = 'X';
327			buf[i++] = '-';
328			sig = sh.sigruntime[SH_SIGRTMAX]-sig;
329		}
330		else
331		{
332			buf[i++] = 'I';
333			buf[i++] = 'N';
334			buf[i++] = '+';
335			sig = sig-sh.sigruntime[SH_SIGRTMIN];
336		}
337	}
338	else if(pfx)
339	{
340		buf[i++] = 'S';
341		buf[i++] = 'I';
342		buf[i++] = 'G';
343	}
344	i += sfsprintf(buf+i, 8, "%d", sig);
345	buf[i] = 0;
346	return buf;
347}
348
349/*
350 * if <flag> is positive, then print signal name corresponding to <flag>
351 * if <flag> is zero, then print all signal names
352 * if <flag> is negative, then print all traps
353 */
354static void sig_list(register Shell_t *shp,register int flag)
355{
356	register const struct shtable2	*tp;
357	register int sig;
358	register char *sname;
359	char name[10];
360	const char *names[SH_TRAP];
361	const char *traps[SH_DEBUGTRAP+1];
362	tp=shtab_signals;
363	if(flag<=0)
364	{
365		/* not all signals may be defined, so initialize */
366		for(sig=shp->sigmax; sig>=0; sig--)
367			names[sig] = 0;
368		for(sig=SH_DEBUGTRAP; sig>=0; sig--)
369			traps[sig] = 0;
370	}
371	for(; *tp->sh_name; tp++)
372	{
373		sig = tp->sh_number&((1<<SH_SIGBITS)-1);
374		if (((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) && (sig = sh.sigruntime[sig-1]+1) == 1)
375			continue;
376		if(sig==flag)
377		{
378			sfprintf(sfstdout,"%s\n",tp->sh_name);
379			return;
380		}
381		else if(sig&SH_TRAP)
382			traps[sig&~SH_TRAP] = (char*)tp->sh_name;
383		else if(sig-- && sig < elementsof(names))
384			names[sig] = (char*)tp->sh_name;
385	}
386	if(flag > 0)
387		sfputr(sfstdout, sig_name(flag-1,name,0), '\n');
388	else if(flag<0)
389	{
390		/* print the traps */
391		register char *trap,**trapcom;
392		sig = shp->st.trapmax;
393		/* use parent traps if otrapcom is set (for $(trap)  */
394		trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom);
395		while(--sig >= 0)
396		{
397			if(!(trap=trapcom[sig]))
398				continue;
399			if(sig > shp->sigmax || !(sname=(char*)names[sig]))
400				sname = sig_name(sig,name,1);
401			sfprintf(sfstdout,trapfmt,sh_fmtq(trap),sname);
402		}
403		for(sig=SH_DEBUGTRAP; sig>=0; sig--)
404		{
405			if(!(trap=shp->st.trap[sig]))
406				continue;
407			sfprintf(sfstdout,trapfmt,sh_fmtq(trap),traps[sig]);
408		}
409	}
410	else
411	{
412		/* print all the signal names */
413		for(sig=1; sig <= shp->sigmax; sig++)
414		{
415			if(!(sname=(char*)names[sig]))
416				sname = sig_name(sig,name,1);
417			sfputr(sfstdout,sname,'\n');
418		}
419	}
420}
421