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