+/*
+ * usage:
+ * .../summon <real-summoner> <calling-nick> <calling-path> <channel>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+#include <utmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static void check(const char *string, const char *what, int maxlen) {
+ int c;
+
+ if (strlen(string) > maxlen) { fprintf(stderr,"%s too long\n",what); exit(8); }
+ while ((c= *string++)) {
+ if (isspace((unsigned char)c) || !isprint((unsigned char)c))
+ { fprintf(stderr,"bad char in %s\n",what); exit(8); }
+ }
+}
+
+static void die(const char *msg) {
+ fprintf(stderr,"%s\n",msg); exit(8);
+}
+
+static void problem(const char *msg) {
+ printf("problem %s\n",msg);
+ exit(0);
+}
+
+enum howbad { hb_notlogon, hb_noterminal, hb_nomessages, hb_ok };
+static enum howbad closest= hb_notlogon;
+
+static time_t best_idle;
+static char best_line[UT_LINESIZE+1];
+
+static void thisis(enum howbad hb) {
+ if (hb > closest) closest= hb;
+}
+
+int main(int argc, const char *const *argv) {
+ struct utmp *ue;
+ const char *myself;
+ struct stat stab;
+ int fd;
+ char idlebuf[20];
+
+ if (argc != 5) die("bad usage");
+
+ myself= getenv("USER");
+ if (!myself) die("USER not set");
+ if (strlen(myself) > UT_NAMESIZE) die("own username too long");
+
+ check(argv[2],"nick",20);
+ check(argv[3],"path",60);
+ check(argv[4],"channel",20);
+
+ if (chdir("/dev")) { perror("chdir /dev"); exit(8); }
+
+ while ((errno=0, ue= getutent())) {
+ if (ue->ut_type != USER_PROCESS) continue;
+ if (strncmp(ue->ut_user,myself,UT_NAMESIZE)) continue;
+ if (!ue->ut_line[0]) { thisis(hb_noterminal); continue; }
+ ue->ut_line[UT_LINESIZE]= 0; /* overflows into next field :-/ */
+ if (stat(ue->ut_line,&stab)) {
+ printf("warning could not stat %s: %s\n",ue->ut_line,strerror(errno));
+ thisis(hb_noterminal); continue;
+ }
+ if (!(stab.st_mode & S_IWGRP)) { thisis(hb_nomessages); continue; }
+ closest= hb_ok;
+ if (closest == hb_ok && stab.st_atime <= best_idle) continue;
+ strcpy(best_line,ue->ut_line);
+ best_idle= stab.st_atime;
+ }
+ if (errno) { perror("getutent set errno"); exit(8); }
+
+ switch (closest) {
+ case hb_notlogon: problem("is not logged on");
+ case hb_noterminal: problem("does not have any terminal/shell sessions");
+ case hb_nomessages: problem("is refusing messages");
+ case hb_ok: break;
+ default: abort();
+ }
+
+ sprintf(idlebuf,"%lu",(unsigned long)best_idle);
+
+ fd= open(best_line, O_NOCTTY|O_NONBLOCK|O_WRONLY);
+ if (fd < 0) {
+ fprintf(stderr,"unable to open terminal (%s): %s",best_line,strerror(errno));
+ exit(8);
+ }
+ if (dup2(fd,0)) { perror("dup2"); exit(8); }
+ execlp(argv[1],argv[1], best_line,idlebuf,argv[2],argv[3],argv[4], (const char*)0);
+ perror(argv[1]); exit(8);
+}