shithub: svc

Download patch

ref: 2b69733d8c24af32a3e89fcf587e6a7409d179d6
parent: ab79325966de7ae86d5ed5f470930f103e252e6a
author: Michael Misch <michaelmisch1985@gmail.com>
date: Sun Feb 15 01:51:44 EST 2026

Add in IRC and some changes required to get it going - not quite ready for use

--- a/client.c
+++ b/client.c
@@ -346,7 +346,7 @@
 				return;
 			}
 			qlock(&b->l);
-			if(f->cl->fd > 0){
+			if(f->cl && f->cl->fd > 0){
 				b->tag = flushtag = 1;
 				rwakeup(&b->rz);
 				close(f->cl->fd);
--- a/convS2C.c
+++ b/convS2C.c
@@ -23,12 +23,19 @@
 convS2C(Cmd *cmd, char *c, uint n)
 {
 	struct state s;
+	int t;
 
 	s.fn = (*parse_cmd);
 	s.cmd.type = ErrorCmd;
 	s.base = c;
 	s.size = n;
-	while((s.fn(&s) > 0));
+	for(;;){
+		t = s.fn(&s);
+		if(t < 0)
+			return t;
+		if(t == 0)
+			break;
+	}
 
 	cmd->type = s.cmd.type;
 	cmd->data = s.cmd.data;
@@ -87,7 +94,7 @@
 				s->cmd.type = MarkdownCmd;
 			else {
 				s->cmd.type = ServiceCmd;
-				snprint(s->cmd.svccmd, n, s->base);
+				snprint(s->cmd.svccmd, n+1, s->base);
 			}
 			s->size -= n;
 			n++;
--- /dev/null
+++ b/irc/irc.c
@@ -1,0 +1,492 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <libsec.h>
+
+#define NPAR 14
+
+enum state {
+	Time,
+	Cmd,
+	Prefix,
+	Middle,
+	Trail,
+	Ok
+};
+
+int enctls = 0; /* ssl/tls */
+int ctlfd;
+int ircfd;
+
+char *realname;
+char *nickname;
+char *server;
+char *svcname;
+
+QLock lk;
+
+typedef struct handler Handler;
+struct handler {
+	char *cmd;
+	int (*fun)(int fd, char *time, char *pre, char *cmd, char *par[]);
+};
+
+int pmsg(int fd, char *time, char *pre, char *cmd, char *par[]);
+int ntc(int fd, char *time, char *pre, char *cmd, char *par[]);
+int generic(int fd, char *time, char *pre, char *cmd, char *par[]);
+int ping(int fd, char *time, char *pre, char *cmd, char *par[]);
+int join(int fd, char *time, char *pre, char *cmd, char *par[]);
+int misc(int fd, char *time, char *pre, char *cmd, char *par[]);
+int topic(int fd, char *time, char *pre, char *cmd, char *par[]);
+int numeric(int fd, char *time, char *pre, char *cmd, char *par[]);
+
+Handler handlers[] = {
+	{"PRIVMSG", pmsg},
+	{"NOTICE", ntc},
+	{"PING", ping},
+	{"PONG", ping},
+	{"JOIN", join},
+	{"PART", misc},
+	{"MODE", misc},
+ 	{"QUIT", misc},
+	{"TOPIC", topic},
+	{"332", numeric},
+	{"333", numeric},
+	{"352", numeric},
+	{"315", numeric},
+	{"311", numeric},
+	{"319", numeric},
+	{"312", numeric},
+	{"320", numeric},
+	{"317", numeric},
+	{"318", numeric},
+	{nil, nil},
+};
+
+typedef struct command Command;
+struct command {
+	char *cmd;
+	int (*fun)(char *buff, char *cmd, char *data);
+};
+
+int icmd(char *buff, char *cmd, char *data);
+int lcmd(char *buff, char *cmd, char *data);
+int jcmd(char *buff, char *cmd, char *data);
+int pcmd(char *buff, char *cmd, char *data);
+int qcmd(char *buff, char *cmd, char *data);
+int gcmd(char *buff, char *cmd, char *data);
+
+Command commands[] = {
+	{"input", icmd},
+	{"join", jcmd},
+	{"list", lcmd},
+	{"part", pcmd},
+	{"quit", qcmd},
+	{nil, nil},	
+};
+
+int srvparse(char *line, char **time, char **pre, char **cmd, char *par[], int npar);
+int usrparse(char *line, char **pre, char **cmd, char **data);
+void ircsrv(void);
+void logger(void);
+void reconnect(void);
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-e] [-r realname] [-n nickname] -s svcname [net!]ircserver[!port]\n", argv0);
+	exits("usage");
+}
+
+void
+killall(void)
+{
+	postnote(PNGROUP, getpid(), "quit");
+	while(waitpid() != -1)
+		;
+	remove(svcname);
+	exits(nil);
+}
+
+void
+die(void *, char*)
+{
+	killall();
+}
+
+void
+main(int argc, char **argv)
+{
+	nickname = getuser();;
+
+	ARGBEGIN{
+	case 'e':
+		enctls = 1;
+		break;
+	case 'r':
+		realname = EARGF(usage());
+		break;
+	case 's':
+		svcname = EARGF(usage());
+		break;
+	case 'n':
+		nickname = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND;
+
+	if(argc != 1 || !svcname)
+		usage();
+
+	server = argv[0];
+	
+	/* Create service */
+	ctlfd = open("/mnt/svc/clone", ORDWR);
+	if(ctlfd < 0)
+		sysfatal("%r");
+
+	reconnect();
+	print("Connection to %s established\n", server);
+
+	if(rfork(RFMEM|RFFDG|RFREND|RFPROC|RFNOTEG|RFCENVG|RFNOWAIT) == 0) {
+		notify(die);
+
+		switch(rfork(RFPROC|RFMEM|RFFDG)) {
+		case -1:
+			sysfatal("rfork: %r");
+		case 0:
+			notify(die);
+			logger();
+			break;
+		default:
+			/* Name service */
+			print("Creating service\n");
+			fprint(ctlfd, "%s\n", svcname);
+			svcname = smprint("/srv/%s", svcname);
+
+			/* Default buffer for server messages */
+			fprint(ctlfd, "create server\n");
+			ircsrv();
+			killall();
+			break;
+		}
+	}
+
+	exits(nil);
+}
+
+static long
+readln(int fd, void *vp, long len)
+{
+	char *b = vp;
+	while(len > 0 && read(fd, b, 1) > 0){
+		if(*b++ == '\n')
+			break;
+		len--;
+	}
+	return b - (char*)vp;
+}
+
+void
+reconnect(void)
+{
+	TLSconn *conn;
+	if(ircfd >= 0)
+		close(ircfd);
+	if((ircfd = dial(netmkaddr(server, "tcp", "6667"), nil, nil, nil)) < 0)
+		sysfatal("dial: %r");
+	if(enctls > 0) {
+		conn = (TLSconn *)mallocz(sizeof *conn, 1);
+		ircfd = tlsClient(ircfd, conn);
+		if(ircfd < 0)
+			sysfatal("tls: %r");
+	}
+	// TODO: factotum
+	//fprint(ircfd, "PASS %s\r\n", passwd);
+	fprint(ircfd, "USER %s nil nil :%s\r\n", nickname, realname);
+	fprint(ircfd, "NICK %s\r\n", nickname);
+}
+
+void
+logger(void)
+{
+	char buf[513];
+	char *time, *pre, *cmd, *par[NPAR];
+	long n;
+
+	for(;;){
+		while((n = readln(ircfd, buf, sizeof(buf)-1)) > 0){
+			buf[n] = 0;
+			if(!srvparse(buf, &time, &pre, &cmd, par, NPAR)) {
+				Handler *hp = handlers;
+				while(hp->cmd != nil) {
+					if(!strcmp(hp->cmd, cmd)) {
+						hp->fun(0, time, pre, cmd, par);
+						break;
+					}
+					++hp;
+				}
+				if(hp->cmd == nil)
+					generic(0, time, pre, cmd, par);
+			}
+		}
+		reconnect();	
+	}
+}
+
+void
+ircsrv(void)
+{
+	char buf[512];
+	char *pre, *cmd, *data;
+	int n;
+
+	for(;;){
+		n = read(ctlfd, buf, sizeof(buf)-1);
+		buf[n] = '\0';
+print("Cmd in: %s\n", buf);
+		 if(!usrparse(buf, &pre, &cmd, &data)){
+			Command *c = commands;
+print("Cmd found: %s\n", cmd);
+			while(c->cmd != nil){
+
+				if(!cistrcmp(c->cmd, cmd)){
+					c->fun(pre, cmd, data);
+					break;
+				}
+				++c;
+			}
+			if(c->cmd == nil){
+				/* Unknown command, pass through to IRC */
+				gcmd(pre, cmd, data);
+			}
+		}
+	}
+}
+
+static char *
+prenick(char *p)
+{
+	char *n = p;
+	if(p != nil){
+		while(*p != '\0' && *p != '!')
+			++p;
+		if(*p != '!')
+			n = nil;
+		*p = '\0';
+	}
+	return n;
+}
+
+int
+pmsg(int, char *, char *pre, char *, char *par[])
+{
+	print("pmsg pre=%s, par=%s\n", pre, par[0]);
+	return 1;
+}
+
+int
+ntc(int, char *, char *pre, char *, char *par[])
+{
+	return fprint(ctlfd, "feed server\nnotice pre=%s par[0]=%s\n", pre, par[0]);
+}
+
+int
+generic(int, char *time, char *pre, char *cmd, char *par[])
+{
+	char *nick = prenick(pre);
+
+	// TODO: Build out the whole message here
+	if(nick != nil)
+		return fprint(ctlfd, "feed server\ngeneric %s %s (%s)\n", time, cmd, nick);
+	else
+		return fprint(ctlfd, "feed server\ngeneric %s %s (%s)\n", time, cmd, par[0]);
+}
+
+int
+ping(int, char *, char *, char *, char *par[])
+{
+	return fprint(ircfd, "PONG %s\r\n", par[0]);
+}
+
+int
+join(int, char *, char *, char *, char *par[])
+{
+	if (cistrcmp(par[0], nickname)) 
+		return fprint(ctlfd, "create %s\n", par[1]);
+	return fprint(ctlfd, "feed %s\n%s\n", par[0], par[1]);
+}
+
+int
+misc(int, char *, char *pre, char *, char *par[])
+{
+	
+	return fprint(ircfd, "feed server\nmisc %s %s\n", pre, par[0]);
+}
+
+int
+numeric(int i, char *time, char *pre, char *cmd, char *par[])
+{
+	/* Start to handle all of these - Important ones */
+print("Numeric: %s %s\n", cmd, pre);
+	return 0;
+}
+
+int
+topic(int, char *, char *pre, char *, char *par[])
+{
+	return fprint(ctlfd, "title %s\n%s %s\n", pre, par[0], par[1]);
+}
+
+int
+icmd(char *buff, char *, char *data)
+{
+	return fprint(ircfd, "PRIVMSG %s :%s\r\n", buff, data);
+}
+
+int
+jcmd(char *buff, char *, char *)
+{
+print("joining %s\n", buff);
+	fprint(ctlfd, "create %s\n", buff);
+	return fprint(ircfd, "JOIN %s\r\n", buff);
+}
+
+int
+pcmd(char *buff, char *, char *)
+{
+	return fprint(ircfd, "PART %s\r\n", buff);
+}
+
+int
+lcmd(char *buff, char *, char *)
+{
+	return fprint(ircfd, "LIST %s\r\n", buff);
+}
+
+int
+qcmd(char *, char *, char *data)
+{
+	fprint(ircfd, "QUIT: %s\r\n", data);
+	fprint(ctlfd, "quit\n");
+	exits(0);
+}
+
+int
+gcmd(char *buff, char *cmd, char *data)
+{
+	/* General command, push to ircfd */
+	return fprint(ircfd, "%s %s :%s\r\n", cmd, buff, data);
+}
+
+int
+usrparse(char *line, char **pre, char **cmd, char **data)
+{
+	char *p;
+
+	enum state st = Cmd;
+	*pre = *data = nil;
+
+	for(*cmd = p = line; *p != '\0'; ++p){
+		switch(st){
+		case Cmd:
+			if(*p == ' '){
+				*p = '\0';
+				*pre = p + 1;
+				st = Prefix;
+			}
+			break;
+		case Prefix:
+			if(*p == '\n' || *p == '\r'){
+				*p = '\0';
+				*data = p + 1;
+				st = Middle;
+			}
+			/* No data */
+			if(*p == '\0'){
+				*p = '\0';
+				st = Ok;
+			}
+			break;
+		case Middle:
+			if(*p == '\r' || *p == '\n' || *p == '\0'){
+				*p = '\0';
+				st = Ok;
+			}
+			break;
+		case Ok:
+			*p = '\0';
+			break;
+		}
+	}
+	return st == Ok ? 0 : 1;
+}
+
+int
+srvparse(char *line, char **time, char **pre, char **cmd, char *par[], int npar)
+{
+	int i;
+	char *p;
+	
+	enum state st = Time;
+
+	*time = *pre = *cmd = nil;
+
+	for(*time = p = line, i = 0; *p != '\0'; ++p){
+		switch(st){
+		case Time:
+			if(*p == ' ') {
+				*p = '\0';
+				*cmd = p + 1;
+				st = Cmd;
+			}
+			break;
+		case Cmd:
+			if(*p == ':') {
+				*p = '\0';
+				*pre = p + 1;
+				st = Prefix;
+			} else if (*p == ' ') {
+				*p = '\0';
+				par[i] = p + 1;
+				st = Middle;
+			}
+			break;
+		case Prefix:
+			if(*p == ' '){
+				*p = '\0';
+				*cmd = p + 1;
+				st = Cmd;
+			}
+			break;
+		case Middle:
+			if(*p == '\r' || *p == '\n'){
+				*p = '\0';
+				st = Ok;
+			} else if (*p == ':'){
+				*p = '\0';
+				par[i] = p + 1;
+				st = Trail;
+			} else if (*p == ' '){
+				*p = '\0';
+				i = (i + 1) % npar;
+				par[i] = p + 1;
+				st = Middle;
+			}
+			break;
+		case Trail:
+			if(*p == '\r' || *p == '\n'){
+				*p = '\0';
+				st = Ok;
+			}
+			break;
+		case Ok:
+			*p = '\0';
+			break;
+		}
+	}
+	par[(i + 1) % npar] = nil;
+	return st == Ok ? 0 : 1;
+}
+
--- /dev/null
+++ b/irc/mkfile
@@ -1,0 +1,11 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/svc
+
+TARG=irc
+
+OFILES=\
+	irc.$O
+
+</sys/src/cmd/mkone
+
--- a/service.c
+++ b/service.c
@@ -340,25 +340,16 @@
 		respond(r, nil);
 		return;
 	case Qctl:
-		/* NOTE: This stays here so we always get a good ID back on the client from the initial read */
-		if(!f->svc->isInitialized) {
-			snprint(buf, sizeof(buf), "%d\n", SERVICEID(f->svc));
-			readstr(r, buf);
-			respond(r, nil);
-			return;
-		} else {
-			/* Wait for any data/command from the client */
-			srvrelease(fs);
-			while((cmd = recvp(f->svc->cmds)) == nil)
-				;
-			srvacquire(fs);
-			if(cmd->type != FlushCmd){
-				n = snprint(r->ofcall.data, r->ifcall.count, "%C", cmd);
-				r->ofcall.count = n;
-			}
-			respond(r, nil);
-			return;
+		/* Wait for any data/command from the client */
+		srvrelease(fs);
+		cmd = recvp(f->svc->cmds);
+		if(cmd && cmd->type != FlushCmd){
+			n = snprint(r->ofcall.data, r->ifcall.count, "%C", cmd);
+			r->ofcall.count = n;
 		}
+		srvacquire(fs);
+		respond(r, nil);
+		return;
 
 	}
 	respond(r, "not implemented");
@@ -382,7 +373,11 @@
 		n = r->ofcall.count = r->ifcall.count;
 		p = mallocz(n, 1);
 		memmove(p, r->ifcall.data, n);
-		convS2C(&cmd, p, n);
+		n = convS2C(&cmd, p, n);
+		if(n <= 0){
+			respond(r, "malformed command");
+			goto Out;
+		}
 		switch(cmd.type){
 		case CloneCmd:
 			if(!f->svc->isInitialized){
@@ -414,7 +409,7 @@
 				}
 				goto Out;
 			}
-			// Ignore, something weird going on.
+			/* Ignore, something weird going on */
 			respond(r, nil);
 			goto Out;
 		case CreateCmd:
--