aboutsummaryrefslogtreecommitdiff
path: root/procps/watch.c
blob: 51b324b444d15a5f9aca0de8312bd88bf8075175 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* vi: set sw=4 ts=4: */
/*
 * Mini watch implementation for busybox
 *
 * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de>
 * Copyrigjt (C) Mar 16, 2003 Manuel Novoa III   (mjn3@codepoet.org)
 *
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
//config:config WATCH
//config:	bool "watch (5.2 kb)"
//config:	default y
//config:	help
//config:	watch is used to execute a program periodically, showing
//config:	output to the screen.

//applet:IF_WATCH(APPLET(watch, BB_DIR_BIN, BB_SUID_DROP))

//kbuild:lib-$(CONFIG_WATCH) += watch.o

//usage:#define watch_trivial_usage
//usage:       "[-n SEC] [-tx] PROG ARGS"
//usage:#define watch_full_usage "\n\n"
//usage:       "Run PROG periodically\n"
//usage:     "\n	-n SEC	Period (default 2)"
//usage:     "\n	-t	Don't print header"
//usage:     "\n	-x	exec(PROG,ARGS) instead of sh -c 'PROG ARGS'"
//usage:
//usage:#define watch_example_usage
//usage:       "$ watch date\n"
//usage:       "Mon Dec 17 10:31:40 GMT 2000\n"
//usage:       "Mon Dec 17 10:31:42 GMT 2000\n"
//usage:       "Mon Dec 17 10:31:44 GMT 2000"

/* BB_AUDIT SUSv3 N/A */
/* BB_AUDIT GNU defects -- only option -n is supported. */

#include "libbb.h"

#define ESC "\033"

// procps 2.0.18:
// watch [-d] [-n seconds]
//   [--differences[=cumulative]] [--interval=seconds] command
//
// procps-3.2.3:
// watch [-dt] [-n seconds]
//   [--differences[=cumulative]] [--interval=seconds] [--no-title] command
//
// (procps 3.x and procps 2.x are forks, not newer/older versions of the same)

int watch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int watch_main(int argc UNUSED_PARAM, char **argv)
{
	duration_t period;
	char *period_str = (char*) "2";
	unsigned opt;
	unsigned width, new_width;
	char *header;
	char **argv0;
	char *cmd;

#if 0 // maybe ENABLE_DESKTOP?
	// procps3 compat - "echo TEST | watch cat" doesn't show TEST:
	close(STDIN_FILENO);
	xopen("/dev/null", O_RDONLY);
#endif

	// "+": stop at first non-option (procps 3.x only); -n NUM
	// at least one param
	opt = getopt32(argv, "^+" "dtn:x" "\0" "-1", &period_str);
	argv += optind;

	// watch from both procps 2.x and 3.x concatenates (unless -x).
	// Example:
	// watch ls -l "a /tmp" "2>&1" - ls won't see "a /tmp" as one param.
	argv0 = argv;
	// If -x, cmd is only used for printing header
#if ENABLE_PLATFORM_MINGW32
	cmd = NULL;
	do {
		cmd = xappendword(cmd, *argv);
	} while (*++argv);
#else
	cmd = xstrdup(*argv);
	while (*++argv)
		xasprintf_inplace(cmd, "%s %s", cmd, *argv);
#endif
	if (opt & (1 << 3))
		argv = argv0; // If -x, restore argv[0] to !NULL

	period = parse_duration_str(period_str);
	width = (unsigned)-1; // make sure first time new_width != width
	header = NULL;
	while (1) {
		// home; clear to the end of screen
		printf(ESC"[H" ESC"[J");
		if (!(opt & 0x2)) { // no -t
			const unsigned time_len = sizeof("1234-67-90 23:56:89");

			// STDERR_FILENO is procps3 compat:
			// "watch ls 2>/dev/null" does not detect tty size
			new_width = get_terminal_width(STDERR_FILENO);
			if (new_width != width) {
				width = new_width;
				free(header);
				header = xasprintf("Every"
					" %"IF_FLOAT_DURATION(".1")DURATION_FMT"s:"
					" %-*s",
					period,
					(int)width, cmd
				);
			}
			if (time_len < width) {
				strftime_YYYYMMDDHHMMSS(
					header + width - time_len,
					time_len,
					/*time_t*:*/ NULL
				);
			}

			// compat: empty line between header and cmd output
			printf("%s\n\n", header);
		}
		fflush_all();
		// TODO: 'real' watch pipes cmd's output to itself
		// and does not allow it to overflow the screen
		// (taking into account linewrap!)
		if (argv[0]) // -x?
			spawn_and_wait(argv); // yes
		else
			system(cmd);
		sleep_for_duration(period);
	}
	return 0; // gcc thinks we can reach this :)
}