Include string.h for strlen and friends
[notion.git] / mod_notionflux / mod_notionflux.c
blob5444053a95f21b884eecfce28cea167c7c5fa070
1 /*
2 * mod_notionflux/mod_notionflux/mod_notionflux.c
4 * Copyright (c) Tuomo Valkonen 2004-2005.
6 * This is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
12 #include <errno.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
23 #include <ioncore/../version.h>
24 #include <ioncore/common.h>
25 #include <ioncore/global.h>
26 #include <ioncore/property.h>
27 #include <libmainloop/select.h>
28 #include <libtu/errorlog.h>
29 #include <libextl/extl.h>
31 #include "notionflux.h"
33 typedef struct{
34 int fd;
35 int ndata;
36 char *data;
37 } Buf;
39 Buf bufs[MAX_SERVED];
41 static int listenfd=-1;
42 static char *listenfile=NULL;
43 static ExtlFn tostringfn;
46 /* Without the 'or nil' kludge tostring may have no parameters. */
47 static const char tostringstr[]=
48 "local arg={...}\n"
49 "local callable=arg[1]\n"
50 "local result=callable()\n"
51 "if type(result)=='string' then\n"
52 " return string.format('%q', result)\n"
53 "else\n"
54 " return tostring(result)\n"
55 "end\n";
57 static void writes(int fd, const char *s)
59 if(s!=NULL)
60 write(fd, s, strlen(s));
64 static void close_conn(Buf *buf)
66 if(buf->fd<0)
67 return;
69 mainloop_unregister_input_fd(buf->fd);
71 close(buf->fd);
72 buf->fd=-1;
73 buf->ndata=0;
74 if(buf->data!=NULL){
75 free(buf->data);
76 buf->data=NULL;
81 static void receive_data(int fd, void *buf_)
83 Buf *buf=(Buf*)buf_;
84 bool end=FALSE;
85 ErrorLog el;
86 ExtlFn fn;
87 int i, n;
88 bool success=FALSE;
90 n=read(fd, buf->data+buf->ndata, MAX_DATA-buf->ndata);
92 if(n==0){
93 warn("Connection closed prematurely.");
94 close_conn(buf);
95 return;
98 if(n<0){
99 if(errno!=EAGAIN && errno!=EINTR){
100 writes(fd, "Error: I/O");
101 close_conn(buf);
103 return;
106 for(i=0; i<n; i++){
107 if(buf->data[buf->ndata+i]=='\0')
108 end=TRUE;
111 buf->ndata+=n;
113 if(!end && buf->ndata+n==MAX_DATA){
114 writes(fd, "Error: too much data\n");
115 close_conn(buf);
116 return;
119 if(!end)
120 return;
122 errorlog_begin(&el);
123 if(extl_loadstring(buf->data, &fn)){
124 char *retstr=NULL;
125 if(extl_call(tostringfn, "f", "s", fn, &retstr)){
126 success=TRUE;
127 writes(fd, "S");
128 writes(fd, retstr);
129 writes(fd, "\n");
130 free(retstr);
132 extl_unref_fn(fn);
134 errorlog_end(&el);
135 if(el.msgs!=NULL && !success){
136 writes(fd, "E");
137 writes(fd, el.msgs);
139 errorlog_deinit(&el);
141 close_conn(buf);
145 static void connection_attempt(int lfd, void *data)
147 int i, fd;
148 struct sockaddr_un from;
149 socklen_t fromlen=sizeof(from);
151 fd=accept(lfd, (struct sockaddr*)&from, &fromlen);
153 if(fd<0){
154 warn_err();
155 return;
158 /* unblock */ {
159 int fl=fcntl(fd, F_GETFL);
160 if(fl!=-1)
161 fl=fcntl(fd, F_SETFL, fl|O_NONBLOCK);
162 if(fl==-1){
163 warn_err();
164 close(fd);
165 return;
169 /* close socket on exec */ {
170 int fl=fcntl(fd, F_GETFD);
171 if(fl!=-1)
172 fl=fcntl(fd, F_SETFD, fl|FD_CLOEXEC);
173 if(fl==-1){
174 warn_err();
175 close(fd);
176 return;
181 for(i=0; i<MAX_SERVED; i++){
182 if(bufs[i].fd<0)
183 break;
186 if(i==MAX_SERVED){
187 writes(fd, "Error: busy\n");
188 close(fd);
189 return;
192 assert(bufs[i].data==NULL && bufs[i].ndata==0);
194 bufs[i].data=ALLOC_N(char, MAX_DATA);
196 if(bufs[i].data!=NULL){
197 if(mainloop_register_input_fd(fd, &(bufs[i]), receive_data)){
198 bufs[i].fd=fd;
199 return;
203 writes(fd, "Error: malloc\n");
204 close(fd);
208 static bool start_listening()
210 struct sockaddr_un addr;
212 listenfile=tmpnam(NULL);
213 if(listenfile==NULL){
214 warn_err();
215 return FALSE;
218 if(strlen(listenfile)>SOCK_MAX){
219 warn("Too long socket path");
220 goto err;
223 listenfd=socket(AF_UNIX, SOCK_STREAM, 0);
224 if(listenfd<0)
225 goto errwarn;
227 if(fchmod(listenfd, S_IRUSR|S_IWUSR)<0)
228 goto errwarn;
230 addr.sun_family=AF_UNIX;
231 strcpy(addr.sun_path, listenfile);
234 int fl=fcntl(listenfd, F_GETFD);
235 if(fl!=-1)
236 fl=fcntl(listenfd, F_SETFD, fl|FD_CLOEXEC);
237 if(fl==-1)
238 goto errwarn;
241 if(bind(listenfd, (struct sockaddr*) &addr,
242 strlen(addr.sun_path)+sizeof(addr.sun_family))<0){
243 goto errwarn;
246 if(listen(listenfd, MAX_SERVED)<0)
247 goto errwarn;
249 if(!mainloop_register_input_fd(listenfd, NULL, connection_attempt))
250 goto err;
252 return TRUE;
254 errwarn:
255 warn_err_obj("mod_notionflux listening socket");
256 err:
257 if(listenfd>=0){
258 close(listenfd);
259 listenfd=-1;
262 /*if(listenfile!=NULL){
263 free(listenfile);
264 listenfile=NULL;
267 return FALSE;
271 void close_connections()
273 int i;
275 if(listenfd>=0){
276 mainloop_unregister_input_fd(listenfd);
277 close(listenfd);
278 listenfd=-1;
281 if(listenfile!=NULL){
282 unlink(listenfile);
283 /*free(listenfile);
284 listenfile=NULL;*/
287 for(i=0; i<MAX_SERVED; i++){
288 if(bufs[i].fd>=0)
289 close_conn(&(bufs[i]));
292 extl_unref_fn(tostringfn);
295 char mod_notionflux_ion_api_version[]=ION_API_VERSION;
297 static Atom flux_socket=None;
299 bool mod_notionflux_init()
301 int i;
302 WRootWin *rw;
304 for(i=0; i<MAX_SERVED; i++){
305 bufs[i].fd=-1;
306 bufs[i].data=NULL;
307 bufs[i].ndata=0;
310 if(!extl_loadstring(tostringstr, &tostringfn))
311 return FALSE;
313 if(!start_listening()){
314 extl_unref_fn(tostringfn);
315 close_connections();
316 return FALSE;
319 flux_socket=XInternAtom(ioncore_g.dpy, "_NOTION_MOD_NOTIONFLUX_SOCKET", False);
321 FOR_ALL_ROOTWINS(rw){
322 xwindow_set_string_property(region_xwindow((WRegion*)rw), flux_socket, listenfile);
325 return TRUE;
329 void mod_notionflux_deinit()
331 WRootWin *rw;
333 if(flux_socket!=None){
334 FOR_ALL_ROOTWINS(rw){
335 XDeleteProperty(ioncore_g.dpy, region_xwindow((WRegion*)rw), flux_socket);
339 close_connections();