This repository was archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit_dump.c
More file actions
206 lines (164 loc) · 4.89 KB
/
Copy pathinit_dump.c
File metadata and controls
206 lines (164 loc) · 4.89 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#include <string.h>
#include "config.h"
#include "init.h"
static char cmdbuf[MAXREPORTCMD];
static char currstr[16];
static char nextstr[16];
/* Run-once entries should only be shown when the init is switching
runlevels, as it may help determining what's going on and why we're not
on the target runlevel yet.
Otherwise, we've got to check both nextlevel *and* currlevel to
show entries that are dying. */
static int shouldbeshown(struct initrec* p)
{
int switching = (currlevel != nextlevel);
if(p->pid > 0) /* anything running is worth showing */
return 1;
if((p->flags & C_ONCE) && !switching)
return 0;
return levelmatch(p, nextlevel) ||
(switching ? levelmatch(p, currlevel) : 0);
}
/* Expected string length of a positive integer (entry pid) */
static int pintlen(int n)
{
int l = 0;
while(n > 0) { l++; n /= 10; }
return l;
}
/* For paused/disabled/failed/signalled entries, we show a sign next to
or instead of its pid. This is the only way for the user to know why
a certain process is not running. */
static char rectag(struct initrec* p)
{
if(p->flags & P_MANUAL)
return '-';
else if(p->flags & P_FAILED)
return 'x';
else if(p->pid < 0)
return '-';
else if(p->flags & (P_SIGTERM | P_SIGKILL))
return '!';
else if(p->flags & P_SIGSTOP)
return '*';
else
return ' ';
}
/* Convert runlevel bitmask into a readable string:
(1<<2) | (1<<a) | (1<<c) -> "1ac"
Telinit will show this as "current runlevel" */
static void rlstr(char* str, int len, int mask)
{
char* p = str;
char* end = str + len - 1;
static char bits[16] = "0123456789abcdef";
int i;
for(i = 0; i < 16 && p < end; i++)
if(mask & (1 << i))
*(p++) = bits[i];
*p = '\0';
}
/* warn() can't handle argv[] directly, and it can be too long to show anyway.
[ "/sbin/vsftpd", "/etc/vsftpd.conf", "-obackground=NO" ] ->
"/sbin/vsftpd /etc/vsftpd.conf -oback..."
The string is written to a temporary buffer, which is then passed
to warn as %s.
Command width is a constant set in config.h. There is no point in trying
to show the whole command, or even as much of the command as possible.
The first few args should be informative enough. */
static void joincmd(char* buf, int len, char** argv)
{
char** arg;
int arglen;
int cpylen;
char* ptr = buf;
if(len < 4) /* "...\0"; should never happen */
goto out;
len--; /* terminating \0 */
for(arg = argv; *arg && len > 0; arg++) {
arglen = strlen(*arg);
cpylen = (arglen <= len ? arglen : len);
strncpy(ptr, *arg, cpylen);
ptr += cpylen;
len -= cpylen;
if(cpylen < arglen) {
break;
} else if(len > 0) {
*(ptr++) = ' ';
len--;
}
} if(*arg) {
ptr += (len > 3 ? 3 : len);
strncpy(ptr - 3, "...", 3);
} else if(ptr > buf && *(ptr-1) == ' ')
ptr--;
out: *ptr = '\0';
/* sanitize the string */
for(ptr = buf; *ptr; ptr++)
if(*ptr < 0x20)
*ptr = ' ';
}
static void dumprec(struct initrec* p, int namewidth, int pidwidth)
{
char tag = rectag(p);
if(p->flags & C_SHELL)
/* C_SHELL implies argv = [ /bin/sh, -c, command, NULL ] */
joincmd(cmdbuf+1, sizeof(cmdbuf)-1, p->argv+2), *cmdbuf = '!';
else
joincmd(cmdbuf, sizeof(cmdbuf), p->argv);
if(p->pid > 0)
warn("%-*s %c%-*i %s",
namewidth, p->name, tag, pidwidth, p->pid, cmdbuf);
else
warn("%-*s %-*c %s",
namewidth, p->name, pidwidth, tag, cmdbuf);
}
/* These two functions provide telinit pidof and telinit ? output
respectively. Both are called from within parsecmd, with warnfd
being the open telinit connection.
This is the only case when warn() is used for non-error output.
The leading # sign is used to pass that to telinit, making
it choose stdout instead of stderr and exit with code 0.
How it should look like:
# telinit pidof sshd
1234
# telinit list
Runlevel 3a
ftpd 546 /usr/bin/vsftpd /etc/vsftpd.conf ...
dropbear 1234 /usr/sbin/dropbear -F -R
ntpd - /usr/sbin/ntpd -g -n
The code below takes some effort to align columns properly,
otherwise the output look really bad. Init is not the place to
do fancy text formatting, but other options are worse in various
ways. */
void dumpidof(struct initrec* p)
{
if(p->pid > 0)
warn("#%i", p->pid);
}
void dumpstate(void)
{
struct initrec *p, **pp;
rlstr(currstr, 16, currlevel);
rlstr(nextstr, 16, nextlevel);
if(currlevel == nextlevel)
warn("#Runlevel %s", currstr);
else
warn("#Switching %s to %s", currstr, nextstr);
/* First pass, get column widths */
int len;
int maxnamelen = 0;
int maxpidlen = 0;
for(pp = cfg->inittab; (p = *pp); pp++) {
if(!shouldbeshown(p))
continue;
if((len = strlen(p->name)) > maxnamelen)
maxnamelen = len;
if((len = pintlen(p->pid)) > maxpidlen)
maxpidlen = len;
}
/* Second pass, do the output */
for(pp = cfg->inittab; (p = *pp); pp++)
if(shouldbeshown(p))
dumprec(p, maxnamelen, maxpidlen);
}