/* * Copyright (c) 2002 by Sergey Lyubka * All rights reserved * * primitive IRC bot */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void edaemon(void); static void usage(const char *); static void dolog(const char *fmt, ...); static void fini(int); static int loop(const char *, short, const char *, const char *, const char *); static void cmd(const char *fmt, ...); static void connbroken(int); static int getip(const char *host, struct in_addr *); /* * Get the IP address by given hostname. * Place it into ap, return 0 on success, -1 on error */ int getip(const char *host, struct in_addr *ap) { struct hostent *hep; if ((hep = gethostbyname(host)) == NULL) { dolog("getip: gethostbyname() failed, errno %d", errno); return (-1); } /* sopy the IP address */ (void) memcpy(ap, hep->h_addr, 4); return (0); } /* * Send a specified command to a server */ void cmd(const char *fmt, ...) { va_list ap; int nbytes; char obuf[BUFSIZ]; va_start(ap, fmt); nbytes = vsnprintf(obuf, sizeof(obuf), fmt, ap); va_end(ap); if (nbytes < (sizeof(obuf) - 3)) { (void) strcat(obuf, "\r\n"); (void) printf("%s", obuf); } else { dolog("cmd: supplied command too long"); } } /* * called when connection to server is lost */ void connbroken(int sig) { dolog("connbroken: connection lost"); /* TODO: set the reconnection flag here, dont exit */ exit(1); } /* * Log using a syslog */ void dolog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); openlog("icrbot", LOG_INFO | LOG_PID | LOG_NDELAY, LOG_DAEMON); vsyslog(LOG_INFO, fmt, ap); closelog(); va_end(ap); } /* * The exit handler */ void fini(int sig) { dolog("Stopped (by signal %d)", sig); _exit(127); } /* * fork into background */ void edaemon(void) { pid_t pid; int fd; struct rlimit rlim; /* ignore all terminal-related signals */ if (getppid() != 1) { signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTSTP, SIG_IGN); } /* fork, and parent exits*/ if ((pid = fork()) != 0) { (void) printf("forked with pid [%d]\n", pid); exit(0); } /* become a session leader */ setsid(); /* close all file descriptors - especially stdin/stdout etc */ getrlimit(RLIMIT_NOFILE, &rlim); for (fd = 0; fd < rlim.rlim_max; fd++) (void) close(fd); /* go to root, so don't make a problems to umount */ (void) chdir("/"); } void usage(const char *progname) { (void) fprintf(stderr, "usage:\n" "%s [-d] [-h ] [-p ] [-o ]\n" "\t[-n ] [-u ] [-c ]\n", progname); exit(1); } /* * Main listening loop */ int loop(const char *host, short port, const char *user, const char *nick, const char *chan) { int sock, quit; struct sockaddr_in sa; char *line; char ibuf[BUFSIZ]; if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { dolog("loop: socket(), errno %d", errno); return (1); } /* create a socket */ sa.sin_family = AF_INET; sa.sin_port = htons(port); if (getip(host, &sa.sin_addr) != 0) { dolog("loop: invalid host %s", host); return (1); } /* connect to the IRC server */ if (connect(sock, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) != 0) { dolog("loop: connect(), errno %d", errno); (void) close(sock); return (1); } /* redirect socket to stdin, stdout */ if (dup2(sock, STDIN_FILENO) == -1) { dolog("loop: dup2(), errno %d", errno); (void) close(sock); return (1); } if (dup2(sock, STDOUT_FILENO) == -1) { dolog("loop: dup2(), errno %d", errno); (void) close(sock); return (1); } /* socket is duplicated to stdin, close the sock */ if (sock != STDOUT_FILENO && sock != STDIN_FILENO) (void) close(sock); cmd("user %s %s %s %s", user, user, user, user); cmd("nick %s", nick); cmd("join %s", chan); /* infinite loop */ for (quit = 0; quit == 0; ) { line = fgets(ibuf, sizeof(ibuf), stdin); if (line == NULL) { dolog("loop: error reading from server, errno %d", ferror(stdin)); quit++; continue; } if (memcmp(line, "PING", 4) == 0) { cmd("PONG %s", host); continue; } /* put message in the log */ (void) fprintf(stderr, "%s", line); } return (0); } int main(int ac, char *const *av) { int ch, debug; extern char *optarg; extern int optind; short port; const char *host, *logfile, *chan, *user, *nick; debug = 0; host = "irc.lucky.net"; logfile = "/tmp/irc.log"; /* use absolute pathname ! */ port = 6667; user = nick = "cbot"; chan = "#c"; while ((ch = getopt(ac, av, "dp:h:o:c:n:u:")) != -1) switch (ch) { case 'd': debug++; /* debug mode */ break; case 'h': host = optarg; /* host */ break; case 'p': port = (short) atoi(optarg); /* port */ break; case 'o': logfile = optarg; /* logfile */ break; case 'c': chan = optarg; /* channel */ break; case 'u': user = optarg; /* user */ break; case 'n': nick = optarg; /* nick */ break; default: usage(av[0]); /* NOTREACHED */ break; } if (ac > optind) usage(av[0]); dolog("Started (%s:%d)", host, port); /* detach from terminal, if not in debug mode */ if (debug == 0) edaemon(); /* Install exit handler, and set the signal handlers */ (void) atexit( (void (*)(void)) fini); (void) signal(SIGINT, fini); (void) signal(SIGTERM, fini); (void) signal(SIGQUIT, fini); (void) signal(SIGPIPE, connbroken); /* redirect the logfile to stderr */ (void) freopen(logfile, "a+", stderr); /* set unbuffered mode to standard streams */ setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); return (loop(host, port, user, nick, chan)); }