chiark / gitweb /
Reorder security checks for !op etc.
[ircbot] / summon.c
CommitLineData
7ce72032
IJ
1/*
2 * usage:
3 * .../summon <real-summoner> <calling-nick> <calling-path> <channel>
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9#include <ctype.h>
10#include <errno.h>
11#include <string.h>
12
13#include <utmp.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <fcntl.h>
17
18static void check(const char *string, const char *what, int maxlen) {
19 int c;
20
21 if (strlen(string) > maxlen) { fprintf(stderr,"%s too long\n",what); exit(8); }
22 while ((c= *string++)) {
23 if (isspace((unsigned char)c) || !isprint((unsigned char)c))
24 { fprintf(stderr,"bad char in %s\n",what); exit(8); }
25 }
26}
27
28static void die(const char *msg) {
29 fprintf(stderr,"%s\n",msg); exit(8);
30}
31
32static void problem(const char *msg) {
33 printf("problem %s\n",msg);
34 exit(0);
35}
36
37enum howbad { hb_notlogon, hb_noterminal, hb_nomessages, hb_ok };
38static enum howbad closest= hb_notlogon;
39
40static time_t best_idle;
41static char best_line[UT_LINESIZE+1];
42
43static void thisis(enum howbad hb) {
44 if (hb > closest) closest= hb;
45}
46
47int main(int argc, const char *const *argv) {
48 struct utmp *ue;
49 const char *myself;
50 struct stat stab;
51 int fd;
52 char idlebuf[20];
53
54 if (argc != 5) die("bad usage");
55
56 myself= getenv("USER");
57 if (!myself) die("USER not set");
58 if (strlen(myself) > UT_NAMESIZE) die("own username too long");
59
60 check(argv[2],"nick",20);
61 check(argv[3],"path",60);
62 check(argv[4],"channel",20);
63
64 if (chdir("/dev")) { perror("chdir /dev"); exit(8); }
65
66 while ((errno=0, ue= getutent())) {
67 if (ue->ut_type != USER_PROCESS) continue;
68 if (strncmp(ue->ut_user,myself,UT_NAMESIZE)) continue;
69 if (!ue->ut_line[0]) { thisis(hb_noterminal); continue; }
70 ue->ut_line[UT_LINESIZE]= 0; /* overflows into next field :-/ */
71 if (stat(ue->ut_line,&stab)) {
72 printf("warning could not stat %s: %s\n",ue->ut_line,strerror(errno));
73 thisis(hb_noterminal); continue;
74 }
75 if (!(stab.st_mode & S_IWGRP)) { thisis(hb_nomessages); continue; }
76 closest= hb_ok;
77 if (closest == hb_ok && stab.st_atime <= best_idle) continue;
78 strcpy(best_line,ue->ut_line);
79 best_idle= stab.st_atime;
80 }
81 if (errno) { perror("getutent set errno"); exit(8); }
82
83 switch (closest) {
84 case hb_notlogon: problem("is not logged on");
85 case hb_noterminal: problem("does not have any terminal/shell sessions");
86 case hb_nomessages: problem("is refusing messages");
87 case hb_ok: break;
88 default: abort();
89 }
90
91 sprintf(idlebuf,"%lu",(unsigned long)best_idle);
92
93 fd= open(best_line, O_NOCTTY|O_NONBLOCK|O_WRONLY);
94 if (fd < 0) {
95 fprintf(stderr,"unable to open terminal (%s): %s",best_line,strerror(errno));
96 exit(8);
97 }
98 if (dup2(fd,0)) { perror("dup2"); exit(8); }
99 execlp(argv[1],argv[1], best_line,idlebuf,argv[2],argv[3],argv[4], (const char*)0);
100 perror(argv[1]); exit(8);
101}