FSF address
[notion.git] / mod_notionflux / mod_notionflux.c
blobc55369c216e63b1b901a1d11fcf30a22caacb5fe
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 <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
22 #include <ioncore/../version.h>
23 #include <ioncore/common.h>
24 #include <ioncore/global.h>
25 #include <ioncore/property.h>
26 #include <libmainloop/select.h>
27 #include <libtu/errorlog.h>
28 #include <libextl/extl.h>
30 #include "notionflux.h"
32 typedef struct{
33 int fd;
34 int ndata;
35 char *data;
36 } Buf;
38 Buf bufs[MAX_SERVED];
40 static int listenfd=-1;
41 static char *listenfile=NULL;
42 static ExtlFn tostringfn;
43 static int tmp[CHUNK];
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 Buf *buf=NULL;
149 struct sockaddr_un from;
150 socklen_t fromlen=sizeof(from);
152 fd=accept(lfd, (struct sockaddr*)&from, &fromlen);
154 if(fd<0){
155 warn_err();
156 return;
159 /* unblock */ {
160 int fl=fcntl(fd, F_GETFL);
161 if(fl!=-1)
162 fl=fcntl(fd, F_SETFL, fl|O_NONBLOCK);
163 if(fl==-1){
164 warn_err();
165 close(fd);
166 return;
170 /* close socket on exec */ {
171 int fl=fcntl(fd, F_GETFD);
172 if(fl!=-1)
173 fl=fcntl(fd, F_SETFD, fl|FD_CLOEXEC);
174 if(fl==-1){
175 warn_err();
176 close(fd);
177 return;
182 for(i=0; i<MAX_SERVED; i++){
183 if(bufs[i].fd<0)
184 break;
187 if(i==MAX_SERVED){
188 writes(fd, "Error: busy\n");
189 close(fd);
190 return;
193 assert(bufs[i].data==NULL && bufs[i].ndata==0);
195 bufs[i].data=ALLOC_N(char, MAX_DATA);
197 if(bufs[i].data!=NULL){
198 if(mainloop_register_input_fd(fd, &(bufs[i]), receive_data)){
199 bufs[i].fd=fd;
200 return;
204 writes(fd, "Error: malloc\n");
205 close(fd);
209 static bool start_listening()
211 struct sockaddr_un addr;
212 int sock;
214 listenfile=tmpnam(NULL);
215 if(listenfile==NULL){
216 warn_err();
217 return FALSE;
220 if(strlen(listenfile)>SOCK_MAX){
221 warn("Too long socket path");
222 goto err;
225 listenfd=socket(AF_UNIX, SOCK_STREAM, 0);
226 if(listenfd<0)
227 goto errwarn;
229 if(fchmod(listenfd, S_IRUSR|S_IWUSR)<0)
230 goto errwarn;
232 addr.sun_family=AF_UNIX;
233 strcpy(addr.sun_path, listenfile);
236 int fl=fcntl(listenfd, F_GETFD);
237 if(fl!=-1)
238 fl=fcntl(listenfd, F_SETFD, fl|FD_CLOEXEC);
239 if(fl==-1)
240 goto errwarn;
243 if(bind(listenfd, (struct sockaddr*) &addr,
244 strlen(addr.sun_path)+sizeof(addr.sun_family))<0){
245 goto errwarn;
248 if(listen(listenfd, MAX_SERVED)<0)
249 goto errwarn;
251 if(!mainloop_register_input_fd(listenfd, NULL, connection_attempt))
252 goto err;
254 return TRUE;
256 errwarn:
257 warn_err_obj("mod_notionflux listening socket");
258 err:
259 if(listenfd>=0){
260 close(listenfd);
261 listenfd=-1;
264 /*if(listenfile!=NULL){
265 free(listenfile);
266 listenfile=NULL;
269 return FALSE;
273 void close_connections()
275 int i;
277 if(listenfd>=0){
278 mainloop_unregister_input_fd(listenfd);
279 close(listenfd);
280 listenfd=-1;
283 if(listenfile!=NULL){
284 unlink(listenfile);
285 /*free(listenfile);
286 listenfile=NULL;*/
289 for(i=0; i<MAX_SERVED; i++){
290 if(bufs[i].fd>=0)
291 close_conn(&(bufs[i]));
294 extl_unref_fn(tostringfn);
297 char mod_notionflux_ion_api_version[]=ION_API_VERSION;
299 static Atom flux_socket=None;
301 bool mod_notionflux_init()
303 int i;
304 WRootWin *rw;
306 for(i=0; i<MAX_SERVED; i++){
307 bufs[i].fd=-1;
308 bufs[i].data=NULL;
309 bufs[i].ndata=0;
312 if(!extl_loadstring(tostringstr, &tostringfn))
313 return FALSE;
315 if(!start_listening()){
316 extl_unref_fn(tostringfn);
317 close_connections();
318 return FALSE;
321 flux_socket=XInternAtom(ioncore_g.dpy, "_NOTION_MOD_NOTIONFLUX_SOCKET", False);
323 FOR_ALL_ROOTWINS(rw){
324 xwindow_set_string_property(region_xwindow((WRegion*)rw), flux_socket, listenfile);
327 return TRUE;
331 void mod_notionflux_deinit()
333 WRootWin *rw;
335 if(flux_socket!=None){
336 FOR_ALL_ROOTWINS(rw){
337 XDeleteProperty(ioncore_g.dpy, region_xwindow((WRegion*)rw), flux_socket);
341 close_connections();