1 #include "whowatch.h"
2 #include "config.h"
3 #include "ulist.h"
4 
5 /* indexes in prot_tab[] */
6 #define	SSH		0
7 #define TELNET		1
8 #define LOCAL		2
9 
10 static struct prot_t prot_tab[] = {
11 #ifdef HAVE_PROCESS_SYSCTL
12 	{ "sshd", 22, 0 }, { "telnetd", 23, 0 }, { "init", -1, 0 }
13 #else
14 	{ "(sshd", 22, 0 }, { "(in.telnetd)", 23, 0 }, { "(init)", -1, 0 }
15 #endif
16 };
17 //static char *hlp = "\001[F1]Help [F9]Menu [ENT]proc all[t]ree [i]dle/cmd [c]md [d]etails [s]ysinfo",
18 static char *hlp = "[ENT]proc all[t]ree [d]etails [s]ysinfo";
19 
20 static u32 nusers;
21 static struct wdgt *self;
22 
u_count(char * name,int p)23 static void u_count(char *name, int p)
24 {
25 	int i;
26 	struct prot_t *t;
27 	nusers += p;
28 	for(i = 0; i < sizeof prot_tab/sizeof(struct prot_t); i++){
29 		t = &prot_tab[i];
30 		if(strncmp(t->s, name, strlen(t->s))) continue;
31 		t->nr += p;
32 	}
33 }
34 
35 /*
36  * After deleting line, update line numbers in each user structure
37  */
update_line(int line)38 void update_line(int line)
39 {
40 	struct user_t *u;
41 	struct list_head *tmp;
42 	list_for_each(tmp, &users_l) {
43 		u = list_entry(tmp, struct user_t, head);
44 		if(u->line > line) u->line--;
45 	}
46 }
47 
48 /*
49  * Create new user structure and fill it
50  */
alloc_user(struct utmpx * entry)51 struct user_t *alloc_user(struct utmpx *entry)
52 {
53 	struct user_t *u;
54 	int ppid;
55 
56 	u = calloc(1, sizeof *u);
57 	if(!u) errx(1, "Cannot allocate memory.");
58 	strncpy(u->name, entry->ut_user, sizeof(entry->ut_user));
59 	strncpy(u->tty, entry->ut_line, sizeof(entry->ut_line));
60 	strncpy(u->host, entry->ut_host, sizeof(entry->ut_host));
61 	u->pid = entry->ut_pid;
62 	if((ppid = get_ppid(u->pid)) == -1)
63 		strncpy(u->parent, "can't access", sizeof u->parent);
64 	else 	strncpy(u->parent, get_name(ppid), sizeof u->parent - 1);
65 	u->line = nusers;
66 	return u;
67 }
68 
new_user(struct utmpx * ut)69 static struct user_t* new_user(struct utmpx *ut)
70 {
71 	struct user_t *u;
72 	u = alloc_user(ut);
73 	list_add(&u->head, &users_l);
74 	u_count(u->parent, LOGIN);
75 	return u;
76 }
77 
uprint(struct user_t * u,struct wdgt * w)78 static void uprint(struct user_t *u, struct wdgt *w)
79 {
80 	int n;
81 	scr_attr_set(w, A_BOLD);
82 	n = snprintf(w->mwin->gbuf, w->mwin->gbsize, "%-14.14s %-9.9s %-6.6s %-19.19s %s",
83 		u->parent, u->name, u->tty, u->host,
84 		toggle?count_idle(u->tty):get_w(u->pid));
85 	scr_maddstr(w, w->mwin->gbuf, u->line, 0, n);
86 }
87 
uredraw(struct wdgt * w)88 void uredraw(struct wdgt *w)
89 {
90 	struct list_head *tmp;
91 	struct user_t *u;
92 	scr_werase(w);
93 	scr_output_start(w);
94 	list_for_each_r(tmp, &users_l) {
95 		u = list_entry(tmp, struct user_t, head);
96 		uprint(u, w);
97 	}
98 	scr_output_end(w);
99 }
100 
101 /*
102  * Gather information about users currently on the machine
103  * Called only at start or restart
104  */
read_utmp(void)105 void read_utmp(void)
106 {
107 	static struct utmpx *entry;
108 	struct user_t *u;
109 
110 	while ((entry = getutxent()) != NULL) {
111 		if(entry->ut_type != USER_PROCESS) continue;
112 		u = new_user(entry);
113 	}
114 	return;
115 }
116 
117 /*
118  * get user entry from specific line (cursor position)
119  */
cursor_user(int line)120 struct user_t *cursor_user(int line)
121 {
122 	struct user_t *u;
123 	struct list_head *h;
124 	DBG("looking for user on line %d", line);
125 //	int line = 0; //current->cursor + current->offset;
126 	list_for_each(h, &users_l) {
127 		u = list_entry(h, struct user_t, head);
128 		if(u->line == line) return u;
129 	}
130 	return 0;
131 }
132 
udel(struct user_t * u,struct wdgt * w)133 static void udel(struct user_t *u, struct wdgt *w)
134 {
135 	scr_delline(w, u->line);
136 	scr_ldeleted(w, u->line);
137 	update_line(u->line);
138 	u_count(u->parent, LOGOUT);
139 	list_del(&u->head);
140 	free(u);
141 }
142 
proc_ucount(void)143 char *proc_ucount(void)
144 {
145 	static char buf[64];
146 
147 	int other = nusers - prot_tab[LOCAL].nr - prot_tab[TELNET].nr - prot_tab[SSH].nr;
148 	snprintf(buf, sizeof buf - 1,"\x1%d users: %d local, %d telnet, %d ssh, %d other\x3",
149 	         nusers, prot_tab[LOCAL].nr, prot_tab[TELNET].nr, prot_tab[SSH].nr, other);
150 	return buf;
151 }
152 
open_wtmp(int * wtmp_fd)153 static void open_wtmp(int *wtmp_fd)
154 {
155 	if((*wtmp_fd = open(WTMP_FILE ,O_RDONLY)) == -1) err_exit(1, "Cannot open wtmp");
156 	if(lseek(*wtmp_fd, 0, SEEK_END) == -1) err_exit(1, "Cannot seek end wtmp");
157 }
158 
159 /*
160  * Check wtmp for logouts or new logins
161  */
check_wtmp(struct wdgt * w)162 static void check_wtmp(struct wdgt *w)
163 {
164 	static int wtmp_fd;
165 	struct user_t *u;
166 	struct list_head *h;
167 	struct utmpx entry;
168 	int i, changed = 0;
169 	if(!wtmp_fd) open_wtmp(&wtmp_fd);
170 
171 	while((i = read(wtmp_fd, &entry, sizeof entry)) > 0){
172 		if (i < sizeof entry) prg_exit("Error reading wtmp");
173 		/* user just logged in */
174 		if(entry.ut_type == USER_PROCESS) {
175 			u = new_user(&entry);
176 			changed = 1;
177 			continue;
178 		}
179 		if(entry.ut_type != DEAD_PROCESS) continue;
180 	/* user just logged out */
181 		list_for_each(h, &users_l) {
182 			u = list_entry(h, struct user_t, head);
183 			if(strncmp(u->tty, entry.ut_line, sizeof(entry.ut_line)))
184 				continue;
185 			udel(u, w);
186 			changed = 1;
187 			break;
188 		}
189 	}
190 	if(!changed) return;
191 }
192 
ulist_hlp(struct wdgt * w)193 static void ulist_hlp(struct wdgt *w)
194 {
195 	DBG("sending %s", hlp);
196 	wmsg_send(w, MCUR_HLP, hlp);
197 }
198 
users_init(void)199 void users_init(void)
200 {
201 	read_utmp();
202 }
203 
ulist_periodic(struct wdgt * w)204 static void ulist_periodic(struct wdgt *w)
205 {
206 	static int i;
207 
208 	if(!i) {
209 		wmsg_send(w, MCUR_HLP, hlp);
210 		i = 1;
211 	}
212 	DBG("ulist periodic");
213 	check_wtmp(w);
214 }
215 
cval(void)216 static void *cval(void)
217 {
218 	static struct user_t *u;
219 
220 	u = cursor_user(self->crsr);
221 	if(u) return &u->pid;
222 	return 0;
223 }
224 
225 /*
226  * Needed for search function. If parent, name, tty, host or command line
227  * matches then returns line number of this user.
228  */
user_search(struct wdgt * w,int t)229 static int user_search(struct wdgt *w, int t)
230 {
231 	struct user_t *u;
232 	struct list_head *h;
233 	int l = w->crsr;
234 
235 	list_for_each(h, &users_l) {
236 		u = list_entry(h, struct user_t, head);
237 		if(!t && u->line <= l) continue;
238 		if(t == 1 && u->line < l) continue;
239 		if(reg_match(u->parent)) goto found;
240 		if(reg_match(u->name)) goto found;
241 		if(reg_match(u->tty)) goto found;
242 		if(reg_match(u->host)) goto found;
243 		if(reg_match(toggle?count_idle(u->tty):get_w(u->pid)))
244 			goto found;
245 	}
246 	return 2;
247 found:
248 	scr_crsr_jmp(w, u->line);
249 	return 1;
250 }
251 
umsgh(struct wdgt * w,int type,struct wdgt * s,void * d)252 static void *umsgh(struct wdgt *w, int type, struct wdgt *s, void *d)
253 {
254 	struct user_t *u;
255 	/* accept it even not visible, ptree can ask about user pid*/
256 	if(type == MWANT_UPID) {
257 		u = cursor_user(w->crsr);
258 		if(u) return &u->pid;
259 		else return 0;
260 	}
261 	if(WIN_HIDDEN(w)) return 0;
262 DBG("ulist responded to type %d", type);
263 	switch(type) {
264 		case MALL_CRSR_REG: w->flags |= WDGT_CRSR_SND; break;
265 		case MALL_CRSR_UNREG: w->flags &= ~ WDGT_CRSR_SND; break;
266 		case MWANT_CRSR_VAL: u = cursor_user(w->crsr);
267 				if(u) return u->name;
268 				else return "No user found";
269 		case MSND_SEARCH:
270 				return (void *)(intptr_t)user_search(w, (u32)d);
271 				break;
272 	}
273 
274 	return 0;
275 }
276 
277 /*
278  * Give up main window if currently printing output there
279  * or start printing if not there
280  */
uswitch(struct wdgt * w,int k,int p)281 static void uswitch(struct wdgt *w, int k, int p)
282 {
283 	if(!WIN_HIDDEN(w)) {
284 		DBG("Deleting ulist from lists");
285 		w->redraw = 0;
286 		w->wrefresh = 0;
287 	}
288 	else {
289 		if(k == 't') return;
290 		ulist_hlp(w);
291 		w->redraw = uredraw;
292 		w->wrefresh = scr_wrefresh;
293 		WNEED_REDRAW(w);
294 		wmsg_send(w, MSND_ESOURCE, euser);
295 	}
296 }
297 
crsr_send(struct wdgt * w)298 static void crsr_send(struct wdgt *w)
299 {
300 	struct user_t *u;
301 
302 	if(!(w->flags & WDGT_CRSR_SND)) return;
303 	u = cursor_user(w->crsr);
304 	if(!u) return;
305 	DBG("sending current name %s", u->name);
306 	wmsg_send(w, MCUR_CRSR, u->name);
307 }
308 
309 
ukeyh(struct wdgt * w,int key)310 static int ukeyh(struct wdgt *w, int key)
311 {
312 	int ret = KEY_SKIPPED;
313 	static int pkey;
314 
315 	/* hide/unhide widget */
316 	if(key == 't' || key == KBD_ENTER) {
317 		uswitch(w, key, pkey);
318 		pkey = key;
319 		return KEY_HANDLED;
320 	}
321 	if(WIN_HIDDEN(w)) return ret;
322 	switch(key) {
323 		default:
324 			ret = scr_keyh(w, key);
325 			if(ret == KEY_HANDLED) crsr_send(w);
326 	}
327 	return ret;
328 }
329 
ulist_reg(struct wdgt * w)330 void ulist_reg(struct wdgt *w)
331 {
332 	w->periodic = ulist_periodic;
333 	w->redraw = uredraw;
334 	w->wrefresh = scr_wrefresh;
335 	w->keyh = ukeyh;
336 	w->msgh = umsgh;
337 	w->mwin->cval = cval;
338 	mwin_msg_on(w);
339 	self = w;
340 }
341 
342