1/*
2 * Copyright (c) 2002, 2008, 2009 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <stdbool.h>
24#include <stdlib.h>
25#include <ctype.h>
26#include <signal.h>
27#include <string.h>
28#include <curses.h>
29#include <errno.h>
30#include "preferences.h"
31#include "userinput.h"
32
33static bool is_empty(const char *s) {
34
35    while(*s) {
36        if(!isspace(*s)) {
37            return false;
38	}
39
40	++s;
41    }
42
43    return true;
44}
45
46static bool is_pid(const char *s) {
47    const char *sp;
48
49    for(sp = s; *sp; ++sp) {
50	if(!isdigit(*sp)) {
51	    return false;
52	}
53    }
54
55    /* If the string was not an empty string, then it contains digits. */
56    if(sp != s) {
57	return true;
58    }
59
60    return false;
61}
62
63static void reset_pid(struct user_input_state *s) {
64    s->buf[0] = '\0';
65    s->offset = 0;
66}
67
68static void signal_pid_completion(void *tinst, struct user_input_state *s) {
69    const char *signame;
70    int sig;
71    int err;
72    uid_t euid;
73    gid_t egid;
74    int saved_errno = 0;
75    pid_t pid;
76
77    if(is_empty(s->buf)) {
78	/*
79	 * Any empty buffer indicates that the user didn't want
80	 * to signal the process after all.
81	 */
82
83	reset_pid(s);
84	user_input_set_state(NULL);
85        return;
86    }
87
88    if(!is_pid(s->buf)) {
89	reset_pid(s);
90	user_input_set_error_state("invalid pid");
91	return;
92    }
93
94    pid = atoi(s->buf);
95
96    reset_pid(s);
97
98    sig = top_prefs_get_signal(&signame);
99
100    /* Temporarily drop permissions. */
101    euid = geteuid();
102    egid = getegid();
103
104    if(-1 == seteuid(getuid())
105       || -1 == setegid(getgid())) {
106	user_input_set_error_state("missing setuid bit");
107	return;
108    }
109
110    err = kill(pid, sig);
111
112    if(-1 == err)
113	saved_errno = errno;
114
115    if(-1 == seteuid(euid)
116       || -1 == setegid(egid)) {
117	user_input_set_error_state("restoring setuid bit");
118	return;
119    }
120
121    switch(saved_errno) {
122    case EINVAL:
123	user_input_set_error_state("invalid signal");
124	return;
125
126    case ESRCH:
127	user_input_set_error_state("invalid pid");
128	return;
129
130    case EPERM:
131	user_input_set_error_state("permission error signaling");
132	return;
133    }
134
135    user_input_set_state(NULL);
136}
137
138static void signal_pid_draw(void *tinst, struct user_input_state *s,
139			    WINDOW *win, int row, int column) {
140    char display[60];
141
142    if(-1 == snprintf(display, sizeof(display), "pid: %s", s->buf)) {
143	user_input_set_error_state("string input too long!");
144	return;
145    }
146
147    mvwaddstr(win, row, column, display);
148}
149
150struct user_input_state top_user_input_signal_pid_state = {
151    .offset = 0,
152    .completion = signal_pid_completion,
153    .draw = signal_pid_draw
154};
155
156static void signal_completion(void *tinst, struct user_input_state *s) {
157
158    if(!strlen(s->buf)) {
159	/* Use the current default. */
160	user_input_set_state(&top_user_input_signal_pid_state);
161	return;
162    }
163
164    if(top_prefs_set_signal_string(s->buf)) {
165	user_input_set_error_state("invalid signal name");
166	return;
167    }
168
169    user_input_set_state(&top_user_input_signal_pid_state);
170}
171
172static void signal_draw(void *tinst, struct user_input_state *s, WINDOW *win,
173		       int row, int column) {
174    char display[60];
175    const char *signame;
176
177    (void)top_prefs_get_signal(&signame);
178
179    if(-1 == snprintf(display, sizeof(display), "signal [%s]: %s",
180		      signame, s->buf)) {
181	user_input_set_error_state("string input too long!");
182	return;
183    }
184
185    mvwaddstr(win, row, column, display);
186}
187
188struct user_input_state top_user_input_signal_state = {
189    .offset = 0,
190    .completion = signal_completion,
191    .draw = signal_draw
192};
193