GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/top/commands.c Lines: 0 112 0.0 %
Date: 2017-11-07 Branches: 0 116 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: commands.c,v 1.32 2017/03/15 04:24:14 deraadt Exp $	 */
2
3
/*
4
 *  Top users/processes display for Unix
5
 *  Version 3
6
 *
7
 * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8
 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
31
/*
32
 *  This file contains the routines that implement some of the interactive
33
 *  mode commands.  Note that some of the commands are implemented in-line
34
 *  in "main".  This is necessary because they change the global state of
35
 *  "top" (i.e.:  changing the number of processes to display).
36
 */
37
38
#include <sys/types.h>
39
#include <stdio.h>
40
#include <err.h>
41
#include <ctype.h>
42
#include <errno.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <signal.h>
46
#include <unistd.h>
47
#include <sys/time.h>
48
#include <sys/resource.h>
49
50
#include "top.h"
51
52
#include "boolean.h"
53
#include "utils.h"
54
#include "machine.h"
55
56
static char    *next_field(char *);
57
static int      scan_arg(char *, int *, char *);
58
static char    *err_string(void);
59
static size_t   str_adderr(char *, size_t, int);
60
static size_t   str_addarg(char *, size_t, char *, int);
61
static int      err_compar(const void *, const void *);
62
63
/*
64
 *  Utility routines that help with some of the commands.
65
 */
66
static char *
67
next_field(char *str)
68
{
69
	char *spaces = " \t";
70
	size_t span;
71
72
	span = strcspn(str, spaces);
73
	if (span == strlen(str))
74
		return (NULL);
75
76
	str += span;
77
	*str++  = '\0';
78
79
	while (strcspn(str, spaces) == 0)
80
		str++;
81
82
	if (*str == '\0')
83
		return (NULL);
84
85
	return(str);
86
}
87
88
/*
89
 * Scan the renice or kill interactive arguments for data and/or errors.
90
 */
91
static int
92
scan_arg(char *str, int *intp, char *nptr)
93
{
94
	int val = 0, bad_flag = 0;
95
	char ch;
96
97
	*nptr = '\0';
98
99
	if (*str == '\0')
100
		return (-1);
101
102
	while ((ch = *str++) != '\0') {
103
		if (isspace((unsigned char)ch))
104
			break;
105
		else if (!isdigit((unsigned char)ch))
106
			bad_flag = 1;
107
		else
108
			val = val * 10 + (ch - '0');
109
110
		*(nptr++) = ch;
111
	}
112
	*nptr = '\0';
113
114
	if (bad_flag == 1)
115
		return(-1);
116
117
	*intp = val;
118
	return (0);
119
}
120
121
/*
122
 *  Some of the commands make system calls that could generate errors.
123
 *  These errors are collected up in an array of structures for later
124
 *  contemplation and display.  Such routines return a string containing an
125
 *  error message, or NULL if no errors occurred.  The next few routines are
126
 *  for manipulating and displaying these errors.  We need an upper limit on
127
 *  the number of errors, so we arbitrarily choose 20.
128
 */
129
130
#define ERRMAX 20
131
132
struct errs	errs[ERRMAX];
133
int		errcnt;
134
static char    *err_toomany = " too many errors occurred";
135
static char    *err_listem =
136
	" Many errors occurred.  Press `e' to display the list of errors.";
137
138
/* These macros get used to reset and log the errors */
139
#define ERR_RESET   errcnt = 0
140
#define ERROR(p, e) \
141
	if (errcnt >= ERRMAX) { \
142
		return(err_toomany); \
143
	} else { \
144
		free(errs[errcnt].arg); \
145
		if ((errs[errcnt].arg = strdup(p)) == NULL) \
146
			err(1, "strdup"); \
147
		errs[errcnt++].err = (e); \
148
	}
149
150
#define STRMAX 80
151
152
/*
153
 *  err_string() - return an appropriate error string.  This is what the
154
 *	command will return for displaying.  If no errors were logged, then
155
 *	return NULL.  The maximum length of the error string is defined by
156
 *	"STRMAX".
157
 */
158
static char *
159
err_string(void)
160
{
161
	int cnt = 0, first = Yes, currerr = -1;
162
	static char string[STRMAX];
163
	struct errs *errp;
164
165
	/* if there are no errors, return NULL */
166
	if (errcnt == 0)
167
		return (NULL);
168
169
	/* sort the errors */
170
	qsort(errs, errcnt, sizeof(struct errs), err_compar);
171
172
	/* need a space at the front of the error string */
173
	string[0] = ' ';
174
	string[1] = '\0';
175
176
	/* loop thru the sorted list, building an error string */
177
	while (cnt < errcnt) {
178
		errp = &(errs[cnt++]);
179
		if (errp->err != currerr) {
180
			if (currerr != -1) {
181
				if (str_adderr(string, sizeof string, currerr) >
182
				    sizeof string - 2)
183
					return (err_listem);
184
185
				/* we know there's more */
186
				(void) strlcat(string, "; ", sizeof string);
187
			}
188
			currerr = errp->err;
189
			first = Yes;
190
		}
191
		if (str_addarg(string, sizeof string, errp->arg, first) >=
192
		    sizeof string)
193
			return (err_listem);
194
195
		first = No;
196
	}
197
198
	/* add final message */
199
	if (str_adderr(string, sizeof string, currerr) >= sizeof string)
200
		return (err_listem);
201
202
	/* return the error string */
203
	return (string);
204
}
205
206
/*
207
 *  str_adderr(str, len, err) - add an explanation of error "err" to
208
 *	the string "str".
209
 */
210
static size_t
211
str_adderr(char *str, size_t len, int err)
212
{
213
	size_t msglen;
214
	char *msg;
215
216
	msg = err == 0 ? "Not a number" : strerror(err);
217
218
	if ((msglen = strlcat(str, ": ", len)) >= len)
219
		return (msglen);
220
221
	return (strlcat(str, msg, len));
222
}
223
224
/*
225
 *  str_addarg(str, len, arg, first) - add the string argument "arg" to
226
 *	the string "str".  This is the first in the group when "first"
227
 *	is set (indicating that a comma should NOT be added to the front).
228
 */
229
static size_t
230
str_addarg(char *str, size_t len, char *arg, int first)
231
{
232
	size_t msglen;
233
234
	if (!first) {
235
		if ((msglen = strlcat(str, ", ", len)) >= len)
236
			return (msglen);
237
	}
238
	return (strlcat(str, arg, len));
239
}
240
241
/*
242
 *  err_compar(p1, p2) - comparison routine used by "qsort"
243
 *	for sorting errors.
244
 */
245
static int
246
err_compar(const void *e1, const void *e2)
247
{
248
	const struct errs *p1 = (const struct errs *) e1;
249
	const struct errs *p2 = (const struct errs *) e2;
250
	int result;
251
252
	if ((result = p1->err - p2->err) == 0)
253
		return (strcmp(p1->arg, p2->arg));
254
	return (result);
255
}
256
257
/*
258
 *  error_count() - return the number of errors currently logged.
259
 */
260
int
261
error_count(void)
262
{
263
	return (errcnt);
264
}
265
266
/*
267
 *  kill_procs(str) - send signals to processes, much like the "kill"
268
 *		command does; invoked in response to 'k'.
269
 */
270
char *
271
kill_procs(char *str)
272
{
273
	int signum = SIGTERM, procnum;
274
	uid_t uid, puid;
275
	char tempbuf[TEMPBUFSIZE];
276
	char *nptr, *tmp;
277
278
	tmp = tempbuf;
279
280
	/* reset error array */
281
	ERR_RESET;
282
283
	/* remember our uid */
284
	uid = getuid();
285
286
	/* skip over leading white space */
287
	while (isspace((unsigned char)*str))
288
		str++;
289
290
	if (*str == '-') {
291
		str++;
292
293
		/* explicit signal specified */
294
		if ((nptr = next_field(str)) == NULL)
295
			return (" kill: no processes specified");
296
297
		if (isdigit((unsigned char)*str)) {
298
			(void) scan_arg(str, &signum, tmp);
299
			if (signum <= 0 || signum >= NSIG)
300
				return (" invalid signal number");
301
		} else {
302
			/* translate the name into a number */
303
			for (signum = 0; signum < NSIG; signum++) {
304
				if (strcasecmp(sys_signame[signum],
305
				    str) == 0)
306
					break;
307
			}
308
309
			/* was it ever found */
310
			if (signum == NSIG)
311
				return (" bad signal name");
312
		}
313
		/* put the new pointer in place */
314
		str = nptr;
315
	}
316
	nptr = tempbuf;
317
	/* loop thru the string, killing processes */
318
	do {
319
		if (scan_arg(str, &procnum, nptr) == -1) {
320
			ERROR(nptr, 0);
321
		} else {
322
			/* check process owner if we're not root */
323
			puid = proc_owner(procnum);
324
			if (puid == (uid_t)(-1)) {
325
				ERROR(nptr, ESRCH);
326
			} else if (uid && (uid != puid)) {
327
				ERROR(nptr, EACCES);
328
			} else if (kill(procnum, signum) == -1) {
329
				ERROR(nptr, errno);
330
			}
331
		}
332
	} while ((str = next_field(str)) != NULL);
333
334
	/* return appropriate error string */
335
	return (err_string());
336
}
337
338
/*
339
 *  renice_procs(str) - change the "nice" of processes, much like the
340
 *		"renice" command does; invoked in response to 'r'.
341
 */
342
char *
343
renice_procs(char *str)
344
{
345
	uid_t uid;
346
	char negate;
347
	int prio, procnum;
348
	char tempbuf[TEMPBUFSIZE];
349
	char *nptr;
350
351
	ERR_RESET;
352
	uid = getuid();
353
354
	/* skip over leading white space */
355
	while (isspace((unsigned char)*str))
356
		str++;
357
358
	/* allow for negative priority values */
359
	if ((negate = (*str == '-')) != 0) {
360
		/* move past the minus sign */
361
		str++;
362
	}
363
364
	nptr = tempbuf;
365
	/* use procnum as a temporary holding place and get the number */
366
	procnum = scan_arg(str, &prio, nptr);
367
368
	/* negate if necessary */
369
	if (negate)
370
		prio = -prio;
371
372
#if defined(PRIO_MIN) && defined(PRIO_MAX)
373
	/* check for validity */
374
	if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
375
		return (" bad priority value");
376
#endif
377
378
	/* move to the first process number */
379
	if ((str = next_field(str)) == NULL)
380
		return (" no processes specified");
381
382
	/* loop thru the process numbers, renicing each one */
383
	do {
384
		if (scan_arg(str, &procnum, nptr) == -1) {
385
			ERROR(nptr, 0);
386
		}
387
		/* check process owner if we're not root */
388
		else if (uid && (uid != proc_owner(procnum))) {
389
			ERROR(nptr, EACCES);
390
		} else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) {
391
			ERROR(nptr, errno);
392
		}
393
	} while ((str = next_field(str)) != NULL);
394
395
	/* return appropriate error string */
396
	return (err_string());
397
}