chiark / gitweb /
topicedit: add a timeout; better error handling
[ircbot.git] / summon.c
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
18 static 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
28 static void die(const char *msg) {
29   fprintf(stderr,"%s\n",msg); exit(8);
30 }
31
32 static void problem(const char *msg) {
33   printf("problem %s\n",msg);
34   exit(0);
35 }
36
37 enum howbad { hb_notlogon, hb_noterminal, hb_nomessages, hb_ok };
38 static enum howbad closest= hb_notlogon;
39
40 static time_t best_idle;
41 static char best_line[UT_LINESIZE+1];
42
43 static void thisis(enum howbad hb) {
44   if (hb > closest) closest= hb;
45 }
46
47 int 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 }