Adding upstream version 0.4.1.
[debian/lxdm.git] / src / lxcom.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdarg.h>
6 #include <signal.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <assert.h>
10 #include <fcntl.h>
11 #include <poll.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <sys/stat.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <sys/ioctl.h>
18 #if !defined(linux) && !defined(__NetBSD__)
19 #include <sys/ucred.h>
20 #endif
21
22 #ifndef SCM_CREDS
23 #define SCM_CREDS SCM_CREDENTIALS
24 #endif
25
26 #ifndef linux
27 # ifndef __NetBSD__
28 # define LXDM_PEER_UID(c) ((c)->cr_uid)
29 # define LXDM_PEER_GID(c) ((c)->cr_groups[0])
30 # else
31 # define LXDM_PEER_UID(c) ((c)->sc_uid)
32 # define LXDM_PEER_GID(c) ((c)->sc_gid)
33 # endif
34 # define LXDM_PEER_PID -1
35 #else
36 # define LXDM_PEER_UID(c) ((c)->uid)
37 # define LXDM_PEER_GID(c) ((c)->gid)
38 # define LXDM_PEER_PID(c) ((c)->pid)
39 #endif
40
41 #ifdef __NetBSD__
42 typedef struct sockcred LXDM_CRED;
43 #else
44 typedef struct ucred LXDM_CRED;
45 #endif
46 #include <glib.h>
47 #include "lxcom.h"
48
49 static const char *sock_path;
50 static int self_client_fd=-1;
51 static int self_server_fd=-1;
52 static int self_source_id=0;
53
54 typedef struct{
55 void *data;
56 int pid;
57 void (*func)(void *data,int pid,int status);
58 }ChildWatch;
59
60 typedef struct{
61 int signal;
62 void *data;
63 void (*func)(void *data,int signal);
64 }SignalHandler;
65
66 typedef struct{
67 int user;
68 void *data;
69 GString *(*func)(void *data,int user,int argc,char **argv);
70 }UserCmd;
71
72 static GSList *child_watch_list;
73 static GSList *signal_handler_list;
74 static GSList *user_cmd_list;
75
76 typedef struct _LXComSource
77 {
78 GSource source;
79 GPollFD poll;
80 }LXComSource;
81
82 typedef GString *(*LXComFunc)(gpointer data,int uid,int pid,int argc,char **argv);
83
84 static gboolean lxcom_prepare (GSource *source,gint *timeout)
85 {
86 *timeout = -1;
87 return FALSE;
88 }
89
90 static gboolean lxcom_check(GSource *source)
91 {
92 return TRUE;
93 }
94
95 static gboolean lxcom_dispatch (GSource *source,GSourceFunc callback,gpointer user_data)
96 {
97 char buf[4096];
98 char ctrl[CMSG_SPACE(sizeof(struct ucred))];
99 struct sockaddr_un peer;
100 struct iovec v={buf,sizeof(buf)};
101 struct msghdr h={&peer,sizeof(peer),&v,1,ctrl,sizeof(ctrl),0};
102 struct cmsghdr *cmptr;
103 int ret;
104
105 while(1)
106 {
107 peer.sun_family=0;
108 ret=recvmsg(self_server_fd,&h,0);
109
110 if(ret<0) break;
111 if(ret>4000) continue;
112 buf[ret]=0;
113 for(cmptr = CMSG_FIRSTHDR(&h);cmptr!=NULL;cmptr=CMSG_NXTHDR(&h,cmptr))
114 {
115 LXDM_CRED *c;
116 int size;
117 int argc;
118 char **argv;
119 GString *res;
120
121 #ifndef __NetBSD__
122 size = sizeof(LXDM_CRED);
123 #else
124 if (cmptr->cmsg_len < SOCKCREDSIZE(0)) break;
125 size = SOCKCREDSIZE(((cred *)CMSG_DATA(cmptr))->sc_ngroups);
126 #endif
127 if (cmptr->cmsg_len != CMSG_LEN(size)) break;
128 if (cmptr->cmsg_level != SOL_SOCKET) break;
129 if (cmptr->cmsg_type != SCM_CREDS) break;
130 c=(LXDM_CRED*)CMSG_DATA(cmptr);
131 if(g_shell_parse_argv(buf,&argc,&argv,NULL))
132 {
133 res=((LXComFunc)callback)(user_data,LXDM_PEER_UID(c),LXDM_PEER_PID(c),argc,argv);
134 g_strfreev(argv);
135 if(res)
136 {
137 do{
138 ret=sendto(self_server_fd,res->str,res->len,0,(struct sockaddr*)&peer,sizeof(peer));
139 }while(ret==-1 && errno==EINTR);
140 g_string_free(res,TRUE);
141 if(ret==-1) perror("sendto");
142 }
143 }
144 break;
145 }
146 }
147
148 return TRUE;
149 }
150
151 static GSourceFuncs lxcom_funcs =
152 {
153 lxcom_prepare,
154 lxcom_check,
155 lxcom_dispatch,
156 NULL
157 };
158
159 static GString *lxcom_func(gpointer data,int uid,int pid,int argc,char **argv)
160 {
161 gboolean deal=FALSE;
162 GSList *p,*n;
163 GString *res=NULL;
164 assert(argc>0 && argv!=NULL);
165
166 do{
167 if(!strcmp(argv[0],"SIGNAL"))
168 {
169 if(argc!=2) break;
170 if((pid==-1 && uid==0) || pid==getpid())
171 {
172 int sig=atoi(argv[1]);
173 if(sig==SIGCHLD)
174 {
175 for(p=child_watch_list;p!=NULL;p=n)
176 {
177 ChildWatch *item=p->data;
178 int status;
179 n=p->next;
180 if(waitpid(item->pid,&status,WNOHANG)>0)
181 {
182 child_watch_list=g_slist_delete_link(child_watch_list,p);
183 item->func(item->data,item->pid,status);
184 g_free(item);
185 }
186 }
187 }
188 else
189 {
190 for(p=signal_handler_list;p!=NULL;p=p->next)
191 {
192 SignalHandler *item=p->data;
193 if(item->signal==sig)
194 {
195 item->func(item->data,sig);
196 break;
197 }
198 }
199 }
200 }
201 deal=TRUE;
202 break;
203 }
204 if(!deal) for(p=user_cmd_list;p!=NULL;p=n)
205 {
206 UserCmd *item=p->data;
207 n=p->next;
208 if(item->user==uid)
209 {
210 res=item->func(item->data,uid,argc,argv);
211 deal=TRUE;
212 break;
213 }
214 }
215 if(!deal) for(p=user_cmd_list;p!=NULL;p=n)
216 {
217 UserCmd *item=p->data;
218 n=p->next;
219 if(item->user==-1)
220 {
221 res=item->func(item->data,uid,argc,argv);
222 deal=TRUE;
223 break;
224 }
225 }
226 }while(0);
227 return res;
228 }
229
230 volatile sig_atomic_t lxcom_last_sig;
231 static void sig_handler(int sig)
232 {
233 lxcom_last_sig=sig;
234 lxcom_raise_signal(sig);
235 }
236
237 static void lxcom_exit_cb(void)
238 {
239 if(sock_path)
240 unlink(sock_path);
241 }
242
243 void lxcom_init(const char *sock)
244 {
245 struct sockaddr_un su;
246 int ret,on=1;
247 struct sigaction action;
248 struct stat st;
249
250 sock_path=sock;
251 atexit(lxcom_exit_cb);
252
253 LXComSource *s=(LXComSource*)g_source_new(&lxcom_funcs,sizeof(LXComSource));
254 g_source_set_callback((GSource*)s,(GSourceFunc)lxcom_func,NULL,NULL);
255
256 unlink(sock);
257 memset(&su,0,sizeof(su));
258 su.sun_family=AF_UNIX;
259 strcpy(su.sun_path,sock);
260 self_server_fd=socket(AF_UNIX,SOCK_DGRAM,0);
261 assert(self_server_fd!=-1);
262 ret=setsockopt(self_server_fd,SOL_SOCKET,SO_PASSCRED,&on,sizeof(on));
263 assert(ret==0);
264 fcntl(self_server_fd,F_SETFL,O_NONBLOCK|fcntl(self_server_fd,F_GETFL));
265 ret=bind(self_server_fd,(struct sockaddr*)&su,sizeof(su));
266 assert(ret==0);
267 self_client_fd=socket(AF_UNIX,SOCK_DGRAM,0);
268 assert(self_client_fd!=-1);
269 fcntl(self_client_fd,F_SETFL,O_NONBLOCK|fcntl(self_client_fd,F_GETFL));
270 ret=connect(self_client_fd,(struct sockaddr*)&su,sizeof(su));
271 assert(ret==0);
272
273 s->poll.fd=self_server_fd;
274 s->poll.events=G_IO_IN;
275 g_source_add_poll((GSource*)s,&s->poll);
276 self_source_id=g_source_attach((GSource*)s,NULL);
277
278 action.sa_handler = sig_handler;
279 sigemptyset (&action.sa_mask);
280 action.sa_flags = SA_NOCLDSTOP;
281 sigaction (SIGCHLD, &action, NULL);
282
283 if(!stat(sock,&st))
284 {
285 chmod(sock,st.st_mode|S_IWOTH|S_IWGRP);
286 }
287 }
288
289 static ssize_t lxcom_write(int s,const void *buf,size_t count)
290 {
291 struct iovec iov[1] ={{(void*)buf,count,}};
292 struct msghdr msg = { 0, 0, iov, 1, 0, 0, 0 };
293 #if !defined(linux) && !defined(__NetBSD__)
294 char ctrl[CMSG_SPACE(sizeof(LXDM_CRED))];
295 struct cmsghdr *cmptr;
296 char *p;
297 int i;
298
299 msg.msg_control = ctrl;
300 msg.msg_controllen = sizeof(ctrl);
301
302 cmptr = CMSG_FIRSTHDR(&msg);
303 cmptr->cmsg_len = CMSG_LEN(sizeof(LXDM_CRED));
304 cmptr->cmsg_level = SOL_SOCKET;
305 cmptr->cmsg_type = SCM_CREDS;
306 p=(char*)CMSG_DATA(cmptr);
307 for(i=0;i<sizeof(LXDM_CRED);i++)
308 p[i]=0;
309 #endif
310 return sendmsg(s,&msg,0);
311 }
312
313 void lxcom_raise_signal(int sig)
314 {
315 char temp[32]="SIGNAL ";
316 int pos=7,val;
317 val=(sig/10)%10;if(val) temp[pos++]='0'+val;
318 val=sig%10;temp[pos++]='0'+val;
319 temp[pos]=0;
320 lxcom_write(self_client_fd,temp,sizeof(temp));
321 }
322
323 gboolean lxcom_send(const char *sock,const char *buf,char **res)
324 {
325 int s;
326 int ret;
327 struct sockaddr_un su;
328 int count=strlen(buf)+1;
329 char *addr=NULL;
330
331 memset(&su,0,sizeof(su));
332 su.sun_family=AF_UNIX;
333 s=socket(AF_UNIX,SOCK_DGRAM,0);
334 assert(s!=-1);
335 fcntl(s,F_SETFL,O_NONBLOCK|fcntl(self_server_fd,F_GETFL));
336 s=socket(AF_UNIX,SOCK_DGRAM,0);
337 assert(s!=-1);
338 fcntl(s,F_SETFL,O_NONBLOCK|fcntl(self_client_fd,F_GETFL));
339 strcpy(su.sun_path,sock);
340 ret=connect(s,(struct sockaddr*)&su,sizeof(su));
341 if(ret!=0)
342 {
343 perror("connect");
344 close(s);
345 return -1;
346 }
347
348 if(res)
349 {
350 #ifdef __linux__
351 su.sun_path[0]=0;
352 sprintf(su.sun_path+1,"/var/run/lxdm/lxdm-%d.sock",getpid());
353 #else
354 addr=g_strdup_printf("/var/run/lxdm/lxdm-%d.sock",getpid());
355 unlink(addr);
356 strcpy(su.sun_path,addr);
357 #endif
358 ret=bind(s,(struct sockaddr*)&su,sizeof(su));
359 if(ret!=0)
360 {
361 close(s);
362 g_free(addr);
363 perror("bind");
364 return FALSE;
365 }
366 }
367
368 ret=lxcom_write(s,buf,count);
369 if(ret!=count)
370 {
371 close(s);
372 if(addr) unlink(addr);
373 g_free(addr);
374 return FALSE;
375 }
376 if(res)
377 {
378 struct pollfd pf;
379 *res=NULL;
380 pf.fd=s;
381 pf.events=POLLIN;
382 pf.revents=0;
383 ret=poll(&pf,1,3000);
384 if(ret==1 && (pf.revents & POLLIN))
385 {
386 ret=ioctl(s,FIONREAD,&count);
387 if(ret==0)
388 {
389 char *p=g_malloc(count+1);
390 ret=recv(s,p,count,0);
391 if(ret>=0)
392 {
393 p[ret]=0;
394 *res=p;
395 }
396 else
397 {
398 g_free(p);
399 }
400 }
401 }
402 }
403 close(s);
404 if(addr) unlink(addr);
405 g_free(addr);
406 return TRUE;
407 }
408
409 int lxcom_add_child_watch(int pid,void (*func)(void*,int,int),void *data)
410 {
411 ChildWatch *item;
412 if(pid<0 || !func)
413 return -1;
414 item=g_new(ChildWatch,1);
415 item->func=func;
416 item->data=data;
417 item->pid=pid;
418 child_watch_list=g_slist_prepend(child_watch_list,item);
419 return 0;
420 }
421
422 int lxcom_del_child_watch(int pid)
423 {
424 GSList *p;
425 for(p=child_watch_list;p!=NULL;p=p->next)
426 {
427 ChildWatch *item=p->data;
428 if(item->pid==pid)
429 {
430 child_watch_list=g_slist_delete_link(child_watch_list,p);
431 g_free(item);
432 return 0;
433 }
434 }
435 return -1;
436 }
437
438 int lxcom_set_signal_handler(int sig,void (*func)(void *,int),void *data)
439 {
440 SignalHandler *item;
441 struct sigaction action;
442 if(sig<=0 || sig>=64 || !func)
443 return -1;
444 if(sig==SIGCHLD)
445 return -1;
446
447 item=g_new(SignalHandler,1);
448 item->data=data;
449 item->signal=sig;
450 item->func=func;
451 signal_handler_list=g_slist_prepend(signal_handler_list,item);
452
453 action.sa_handler = sig_handler;
454 sigemptyset (&action.sa_mask);
455 action.sa_flags = 0;
456 sigaction (sig, &action, NULL);
457
458 return 0;
459 }
460
461 int lxcom_add_cmd_handler(int user,GString *(*func)(void *,int,int,char **),void *data)
462 {
463 UserCmd *item;
464 if(!func)
465 return -1;
466 item=g_new(UserCmd,1);
467 item->data=data;
468 item->user=user;
469 item->func=func;
470 user_cmd_list=g_slist_prepend(user_cmd_list,item);
471 return 0;
472 }
473
474 int lxcom_del_cmd_handler(int user)
475 {
476 GSList *p;
477 for(p=user_cmd_list;p!=NULL;p=p->next)
478 {
479 UserCmd *item=p->data;
480 if(item->user==user)
481 {
482 user_cmd_list=g_slist_remove_link(user_cmd_list,p);
483 g_free(item);
484 return 0;
485 }
486 }
487 return -1;
488 }
489
490 #if 0
491
492 #include <poll.h>
493
494 void poll_sock(void)
495 {
496 struct pollfd pf;
497 pf.fd=self_server_fd;
498 pf.events=POLLIN;
499 pf.revents=0;
500 poll(&pf,1,-1);
501 printf("%x\n",pf.revents);
502 }
503
504 void on_sigint(void *data,int sig)
505 {
506 printf("signal SIGINT %s\n",(char*)data);
507 exit(0);
508 }
509
510 int cmd_count;
511 GString *on_cmd(void *data,int user,int arc,char **arg)
512 {
513 int i;
514 printf("%d %s: ",user,(char*)data);
515 for(i=0;i<arc;i++)
516 {
517 printf("%s ",arg[i]);
518 }
519 printf("\n");
520 cmd_count++;
521
522 if(cmd_count==2)
523 lxcom_del_cmd_handler(user);
524 return g_string_new("OK");
525 }
526
527 int main(int arc,char *arg[])
528 {
529 if(arc==1)
530 {
531 GMainLoop *loop;
532 lxcom_init("/tmp/test.sock");
533 lxcom_set_signal_handler(SIGINT,on_sigint,"sigtest");
534 lxcom_add_cmd_handler(-1,on_cmd,"cmdtest");
535 loop=g_main_loop_new(NULL,0);
536 g_main_loop_run(loop);
537 }
538 else
539 {
540 gboolean ret;
541 char *res=0;
542 ret=lxcom_send("/tmp/test.sock",arg[1],&res);
543 if(ret && res) printf("%s\n",res);
544 }
545 return 0;
546 }
547 #endif
548