2010-02-13 Jb Evain <jbevain@novell.com>
[mono-project.git] / mono / io-layer / daemon.c
blobb5ff32afc11bb45751949388226d6298ad6478f8
1 /*
2 * daemon.c: The handle daemon
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <signal.h>
21 #include <sys/wait.h>
22 #include <string.h>
23 #include <sys/time.h>
25 #ifdef HAVE_POLL
26 #include <sys/poll.h>
27 #endif
29 #include <mono/io-layer/io-layer.h>
30 #include <mono/io-layer/handles-private.h>
31 #include <mono/io-layer/wapi-private.h>
32 #include <mono/io-layer/daemon-messages.h>
33 #include <mono/io-layer/timefuncs-private.h>
34 #include <mono/io-layer/daemon-private.h>
35 #include <mono/io-layer/socket-wrappers.h>
37 #define LOGDEBUG(...)
38 #undef DEBUG
39 // #define LOGDEBUG(...) g_message(__VA_ARGS__)
41 /* The shared thread codepath doesn't seem to work yet... */
42 #undef _POSIX_THREAD_PROCESS_SHARED
44 /* Keep track of the number of clients */
45 static int nfds=0;
47 /* Arrays to keep track of channel data for the
48 * daemon and clients indexed by file descriptor
49 * value.
52 typedef struct _channel_data {
53 int io_source; /* the ID given back by g_io_add_watch */
54 guint32 *open_handles; /* array of open handles for this client */
55 } ChannelData;
57 static ChannelData *daemon_channel_data=NULL;
58 static ChannelData *channels=NULL;
59 static int channels_length=0;
61 /* The socket which we listen to new connections on */
62 static int main_sock;
64 /* Set to TRUE by the SIGCHLD signal handler */
65 static volatile gboolean check_processes=FALSE;
67 /* The file_share_hash is used to emulate the windows file sharing mode */
68 typedef struct _share_key
70 dev_t device;
71 ino_t inode;
72 } ShareKey;
74 typedef struct _share_data
76 guint32 sharemode;
77 guint32 access;
78 } ShareData;
80 static GHashTable *file_share_hash = NULL;
82 static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
83 gpointer data);
84 static void check_sharing (dev_t device, ino_t inode);
86 /* Deletes the shared memory segment. If we're exiting on error,
87 * clients will get EPIPEs.
89 static void cleanup (void)
91 int i;
93 #ifdef NEED_LINK_UNLINK
94 unlink(_wapi_shared_data[0]->daemon);
95 #endif
96 for(i=1; i<_wapi_shared_data[0]->num_segments; i++) {
97 unlink (_wapi_shm_file (WAPI_SHM_DATA, i));
99 unlink (_wapi_shm_file (WAPI_SHM_DATA, 0));
101 /* There's only one scratch file */
102 unlink (_wapi_shm_file (WAPI_SHM_SCRATCH, 0));
105 /* If there is only one socket, and no child processes, we can exit.
106 * We test for child processes by counting handle references held by
107 * the daemon.
109 static void maybe_exit (void)
111 guint32 i;
113 LOGDEBUG ("%s: Seeing if we should exit", __func__);
115 if(nfds>1) {
116 LOGDEBUG ("%s: Still got clients", __func__);
117 return;
120 /* Prevent new clients from connecting... */
121 _wapi_shared_data[0]->daemon_running=DAEMON_CLOSING;
123 for(i=0;
124 i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT;
125 i++) {
126 if(daemon_channel_data->open_handles[i]>0) {
127 LOGDEBUG ("%s: Still got handle references", __func__);
128 _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING;
129 return;
133 #ifdef HAVE_POLL
134 /* Last check, make sure no client came along while we were
135 * checking the handle lists.
137 * Use poll() directly here, as glib doesn't seem to have any
138 * exposed way of seeing if a file descriptor is ready
139 * (g_io_channel_get_buffer_condition() isn't it.)
141 * Crappy systems that don't have poll() will just have to
142 * lump it (for them there is still the very slight chance
143 * that someone tried to connect just as the DAEMON_CLOSING
144 * flag was being set.)
147 struct pollfd fds[1];
149 fds[0].fd=main_sock;
150 fds[0].events=POLLIN;
151 fds[0].revents=0;
153 LOGDEBUG ("%s: Last connect check", __func__);
155 if(poll (fds, 1, 0)>0) {
156 /* Someone did connect, so carry on running */
157 LOGDEBUG ("%s: Someone connected", __func__);
159 _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING;
160 return;
163 #endif
165 LOGDEBUG ("%s: Byebye", __func__);
167 cleanup ();
168 exit (0);
172 * signal_handler:
173 * @unused: unused
175 * Called if daemon receives a SIGTERM or SIGINT
177 static void signal_handler (int signo)
179 LOGDEBUG ("%s: daemon received signal %d", __func__, signo);
181 cleanup ();
182 exit (-1);
186 * sigchld_handler:
187 * @unused: unused
189 * Called if daemon receives a SIGCHLD, and notes that a process needs
190 * to be wait()ed for.
192 static void sigchld_handler (int unused)
194 /* Notice that a child process died */
195 check_processes=TRUE;
198 static guint sharedata_hash (gconstpointer key)
200 ShareKey *sharekey = (ShareKey *)key;
202 return(g_int_hash (&(sharekey->inode)));
205 static gboolean sharedata_equal (gconstpointer a, gconstpointer b)
207 ShareKey *share_a = (ShareKey *)a;
208 ShareKey *share_b = (ShareKey *)b;
210 return(share_a->device == share_b->device &&
211 share_a->inode == share_b->inode);
214 /* Catch this here rather than corrupt the shared data at runtime */
215 #if MONO_SIZEOF_SUNPATH==0
216 #error configure failed to discover size of unix socket path
217 #endif
220 * startup:
222 * Bind signals, attach to shared memory and set up any internal data
223 * structures needed.
225 static void startup (void)
227 struct sigaction sa;
229 sa.sa_handler=signal_handler;
230 sigemptyset (&sa.sa_mask);
231 sa.sa_flags=0;
232 sigaction (SIGINT, &sa, NULL);
233 sigaction (SIGTERM, &sa, NULL);
235 #ifndef HAVE_MSG_NOSIGNAL
236 sa.sa_handler=SIG_IGN;
237 sigaction (SIGPIPE, &sa, NULL);
238 #endif
240 sa.sa_handler=sigchld_handler;
241 sa.sa_flags=SA_NOCLDSTOP;
242 sigaction (SIGCHLD, &sa, NULL);
244 #ifdef NEED_LINK_UNLINK
245 /* Here's a more portable method... */
246 snprintf (_wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH-1,
247 "/tmp/mono-handle-daemon-%d-%ld-%ld", getuid (), random (),
248 time (NULL));
249 #else
250 /* Leave the first byte NULL so we create the socket in the
251 * abstrace namespace, not on the filesystem. (Lets see how
252 * portable _that_ is :)
254 * The name is intended to be unique, not cryptographically
255 * secure...
257 snprintf (_wapi_shared_data[0]->daemon+1, MONO_SIZEOF_SUNPATH-2,
258 "mono-handle-daemon-%d-%d-%ld", getuid (), getpid (),
259 time (NULL));
260 #endif
262 file_share_hash = g_hash_table_new_full (sharedata_hash,
263 sharedata_equal, g_free,
264 g_free);
269 * ref_handle:
270 * @channel_data: Channel data for calling client
271 * @handle: handle to inc refcnt
273 * Increase ref count of handle for the calling client. Handle 0 is
274 * ignored.
276 static void ref_handle (ChannelData *channel_data, guint32 handle)
278 guint32 segment, idx;
280 if(handle==0) {
281 return;
284 _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
286 _wapi_shared_data[segment]->handles[idx].ref++;
287 channel_data->open_handles[handle]++;
289 LOGDEBUG ("%s: handle 0x%x ref now %d (%d this process)", __func__, handle,
290 _wapi_shared_data[segment]->handles[idx].ref,
291 channel_data->open_handles[handle]);
295 * unref_handle:
296 * @channel_data: Channel data for calling client
297 * @handle: handle to inc refcnt
299 * Decrease ref count of handle for the calling client. If global ref
300 * count reaches 0 it is free'ed. Return TRUE if the local ref count
301 * is 0. Handle 0 is ignored.
303 static gboolean unref_handle (ChannelData *channel_data, guint32 handle)
305 gboolean destroy=FALSE;
306 guint32 segment, idx;
308 if(handle==0) {
309 return(FALSE);
312 if (channel_data->open_handles[handle] == 0) {
313 g_warning("%s: unref on %d called when ref was already 0", __func__, handle);
314 return TRUE;
317 _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
319 _wapi_shared_data[segment]->handles[idx].ref--;
320 channel_data->open_handles[handle]--;
322 LOGDEBUG ("%s: handle 0x%x ref now %d (%d this process)", __func__, handle,
323 _wapi_shared_data[segment]->handles[idx].ref,
324 channel_data->open_handles[handle]);
326 if (_wapi_shared_data[segment]->handles[idx].ref == 0) {
327 gboolean was_file;
328 dev_t device = 0;
329 ino_t inode = 0;
331 /* This client has released the handle */
332 destroy=TRUE;
334 if (channel_data->open_handles[handle]!=0) {
335 g_warning ("%s: per-process open_handles mismatch, set to %d, should be 0",__func__, channel_data->open_handles[handle]);
338 LOGDEBUG ("%s: Destroying handle 0x%x", __func__, handle);
340 /* if this was a file handle, save the device and
341 * inode numbers so we can scan the share info data
342 * later to see if the last handle to a file has been
343 * closed, and delete the data if so.
345 was_file = (_wapi_shared_data[segment]->handles[idx].type == WAPI_HANDLE_FILE);
346 if (was_file) {
347 struct _WapiHandle_file *file_handle;
348 gboolean ok;
350 ok = _wapi_lookup_handle (GUINT_TO_POINTER (handle),
351 WAPI_HANDLE_FILE,
352 (gpointer *)&file_handle,
353 NULL);
354 if (ok == FALSE) {
355 g_warning ("%s: error looking up file handle %x", __func__, handle);
356 } else {
357 device = file_handle->device;
358 inode = file_handle->inode;
362 _wapi_handle_ops_close_shared (GUINT_TO_POINTER (handle));
364 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
365 mono_mutex_destroy (&_wapi_shared_data[segment]->handles[idx].signal_mutex);
366 pthread_cond_destroy (&_wapi_shared_data[segment]->handles[idx].signal_cond);
367 #endif
369 memset (&_wapi_shared_data[segment]->handles[idx].u, '\0', sizeof(_wapi_shared_data[segment]->handles[idx].u));
370 _wapi_shared_data[segment]->handles[idx].type=WAPI_HANDLE_UNUSED;
372 if (was_file) {
373 check_sharing (device, inode);
377 if(channel_data == daemon_channel_data) {
378 /* The daemon released a reference, so see if it's
379 * ready to exit
381 maybe_exit ();
384 return(destroy);
388 * add_fd:
389 * @fd: Filehandle to add
391 * Create a new GIOChannel, and add it to the main loop event sources.
393 static void add_fd(int fd, GMainContext *context)
395 GIOChannel *io_channel;
396 GSource *source;
397 guint32 *refs;
399 io_channel=g_io_channel_unix_new (fd);
401 /* Turn off all encoding and buffering crap */
402 g_io_channel_set_encoding (io_channel, NULL, NULL);
403 g_io_channel_set_buffered (io_channel, FALSE);
405 refs=g_new0 (guint32,_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
407 if(fd>=channels_length) {
408 /* Add a bit of padding, so we dont resize for _every_
409 * new connection
411 int old_len=channels_length * sizeof(ChannelData);
413 channels_length=fd+10;
414 if(channels==NULL) {
415 channels=g_new0 (ChannelData, channels_length);
416 /* We rely on the daemon channel being created first.
417 * That's safe, because every other channel is the
418 * result of an accept() on the daemon channel.
420 daemon_channel_data = &channels[fd];
421 } else {
422 int daemon_index=daemon_channel_data - channels;
424 /* Can't use g_renew here, because the unused
425 * elements must be NULL and g_renew doesn't
426 * initialise the memory it returns
428 channels=_wapi_g_renew0 (channels, old_len, channels_length * sizeof(ChannelData));
429 daemon_channel_data = channels + daemon_index;
434 channels[fd].open_handles=refs;
436 source = g_io_create_watch (io_channel,
437 G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
438 g_source_set_callback (source, (GSourceFunc)fd_activity,
439 context, NULL);
440 channels[fd].io_source=g_source_attach (source, context);
441 g_source_unref (source);
443 nfds++;
447 * rem_fd:
448 * @channel: GIOChannel to close
450 * Closes the IO channel. Closes all handles that it may have open. If
451 * only main_sock is left, the daemon is shut down.
453 static void rem_fd(GIOChannel *channel, ChannelData *channel_data)
455 guint32 handle_count;
456 int i, j, fd;
458 fd=g_io_channel_unix_get_fd (channel);
460 if(fd == main_sock) {
461 /* We shouldn't be deleting the daemon's fd */
462 g_warning ("%s: Deleting daemon fd!", __func__);
463 cleanup ();
464 exit (-1);
467 LOGDEBUG ("%s: Removing client fd %d", __func__, fd);
469 if (channel_data->io_source == 0) {
470 LOGDEBUG ("%s: channel already closed for fd %d", __func__, fd);
471 return;
475 g_io_channel_shutdown (channel, TRUE, NULL);
476 g_source_remove (channel_data->io_source);
477 g_io_channel_unref (channel);
479 for(i=0;
480 i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT;
481 i++) {
482 handle_count=channel_data->open_handles[i];
484 for(j=0; j<handle_count; j++) {
485 LOGDEBUG ("%s: closing handle 0x%x for client at index %d", __func__, i, g_io_channel_unix_get_fd (channel));
486 /* Ignore the hint to the client to destroy
487 * the handle private data
489 unref_handle (channel_data, i);
493 g_free (channel_data->open_handles);
494 channel_data->open_handles=NULL;
495 channel_data->io_source=0;
497 nfds--;
498 if(nfds==1) {
499 /* Just the master socket left, so see if we can
500 * cleanup and exit
502 maybe_exit ();
506 static void sharemode_set (dev_t device, ino_t inode, guint32 sharemode,
507 guint32 access)
509 ShareKey *sharekey;
510 ShareData *sharedata;
512 sharekey = g_new (ShareKey, 1);
513 sharekey->device = device;
514 sharekey->inode = inode;
516 sharedata = g_new (ShareData, 1);
517 sharedata->sharemode = sharemode;
518 sharedata->access = access;
520 /* Setting share mode to include all access bits is really
521 * removing the share info
523 if (sharemode == (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)) {
524 g_hash_table_remove (file_share_hash, sharekey);
525 } else {
526 g_hash_table_insert (file_share_hash, sharekey, sharedata);
530 static gboolean sharemode_get (dev_t device, ino_t inode, guint32 *sharemode,
531 guint32 *access)
533 ShareKey sharekey;
534 ShareData *sharedata;
536 sharekey.device = device;
537 sharekey.inode = inode;
539 sharedata = (ShareData *)g_hash_table_lookup (file_share_hash,
540 &sharekey);
541 if (sharedata == NULL) {
542 return(FALSE);
545 *sharemode = sharedata->sharemode;
546 *access = sharedata->access;
548 return(TRUE);
551 static gboolean share_compare (gpointer handle, gpointer user_data)
553 struct _WapiHandle_file *file_handle;
554 gboolean ok;
555 ShareKey *sharekey = (ShareKey *)user_data;
557 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
558 (gpointer *)&file_handle, NULL);
559 if (ok == FALSE) {
560 g_warning ("%s: error looking up file handle %p", __func__, handle);
561 return(FALSE);
564 if (file_handle->device == sharekey->device &&
565 file_handle->inode == sharekey->inode) {
566 LOGDEBUG ("%s: found one, handle %p", __func__, handle);
567 return(TRUE);
568 } else {
569 return(FALSE);
573 static void check_sharing (dev_t device, ino_t inode)
575 ShareKey sharekey;
576 gpointer file_handle;
578 LOGDEBUG ("%s: Checking if anything has (dev 0x%llx, inode %lld) still open", __func__, device, inode);
580 sharekey.device = device;
581 sharekey.inode = inode;
583 file_handle = _wapi_search_handle (WAPI_HANDLE_FILE, share_compare,
584 &sharekey, NULL, NULL);
586 if (file_handle == NULL) {
587 /* Delete this share info, as the last handle to it
588 * has been closed
590 LOGDEBUG ("%s: Deleting share data for (dev 0x%llx inode %lld)", __func__, device, inode);
592 g_hash_table_remove (file_share_hash, &sharekey);
596 static gboolean process_compare (gpointer handle, gpointer user_data)
598 struct _WapiHandle_process *process_handle;
599 gboolean ok;
600 pid_t pid;
601 guint32 segment, idx;
603 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
604 (gpointer *)&process_handle, NULL);
605 if(ok==FALSE) {
606 g_warning ("%s: error looking up process handle %p", __func__, handle);
607 return(FALSE);
610 _wapi_handle_segment (handle, &segment, &idx);
611 if (_wapi_shared_data[segment]->handles[idx].signalled) {
612 return(FALSE);
615 pid=GPOINTER_TO_UINT (user_data);
616 if(process_handle->id==pid) {
617 return(TRUE);
618 } else {
619 return(FALSE);
623 static gboolean process_thread_compare (gpointer handle, gpointer user_data)
625 struct _WapiHandle_thread *thread_handle;
626 gboolean ok;
627 guint32 segment, idx;
629 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
630 (gpointer *)&thread_handle, NULL);
631 if(ok==FALSE) {
632 g_warning ("%s: error looking up thread handle %p", __func__, handle);
633 return(FALSE);
636 if(thread_handle->process_handle==user_data) {
637 /* Signal the handle. Don't use
638 * _wapi_handle_set_signal_state() unless we have
639 * process-shared pthread support.
641 LOGDEBUG ("%s: Set thread handle %p signalled, because its process died", __func__, handle);
643 thread_handle->exitstatus=0;
645 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
646 _wapi_handle_lock_handle (handle);
647 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
648 _wapi_handle_unlock_handle (handle);
649 #else
650 /* Just tweak the signal state directly. This is not
651 * recommended behaviour, but it works for threads
652 * because they can never become unsignalled. There
653 * are some nasty kludges in the handle waiting code
654 * to cope with missing condition signals for when
655 * process-shared pthread support is missing.
657 _wapi_handle_segment (handle, &segment, &idx);
658 _wapi_shared_data[segment]->handles[idx].signalled=TRUE;
659 #endif /* _POSIX_THREAD_PROCESS_SHARED */
662 /* Return false to keep searching */
663 return(FALSE);
666 /* Find the handle associated with pid, mark it dead and record exit
667 * status. Finds all thread handles associated with this process
668 * handle, and marks those signalled too, with exitstatus '0'. It
669 * also drops the daemon's reference to the handle, and the thread
670 * pointed at by main_thread.
672 static void process_post_mortem (pid_t pid, int status)
674 gpointer process_handle;
675 struct _WapiHandle_process *process_handle_data;
676 guint32 segment, idx;
678 process_handle=_wapi_search_handle (WAPI_HANDLE_PROCESS,
679 process_compare,
680 GUINT_TO_POINTER (pid),
681 (gpointer *)&process_handle_data,
682 NULL);
683 if(process_handle==0) {
685 * This may happen if we use Process.EnableRaisingEvents +
686 * process.Exited event and the parent has finished.
688 LOGDEBUG ("%s: Couldn't find handle for process %d!", __func__, pid);
689 } else {
690 /* Signal the handle. Don't use
691 * _wapi_handle_set_signal_state() unless we have
692 * process-shared pthread support.
694 struct timeval tv;
696 LOGDEBUG ("%s: Set process %d exitstatus to %d", __func__, pid,
697 WEXITSTATUS (status));
699 /* If the child terminated due to the receipt of a signal,
700 * the exit status must be based on WTERMSIG, since WEXITSTATUS
701 * returns 0 in this case.
703 if (WIFSIGNALED(status))
704 process_handle_data->exitstatus=128 + WTERMSIG (status);
705 else
706 process_handle_data->exitstatus=WEXITSTATUS (status);
708 /* Ignore errors */
709 gettimeofday (&tv, NULL);
710 _wapi_timeval_to_filetime (&tv,
711 &process_handle_data->exit_time);
713 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
714 _wapi_handle_lock_handle (process_handle);
715 _wapi_handle_set_signal_state (process_handle, TRUE, TRUE);
716 _wapi_handle_unlock_handle (process_handle);
717 #else
718 /* Just tweak the signal state directly. This is not
719 * recommended behaviour, but it works for processes
720 * because they can never become unsignalled. There
721 * are some nasty kludges in the handle waiting code
722 * to cope with missing condition signals for when
723 * process-shared pthread support is missing.
725 _wapi_handle_segment (process_handle, &segment, &idx);
726 _wapi_shared_data[segment]->handles[idx].signalled=TRUE;
727 #endif /* _POSIX_THREAD_PROCESS_SHARED */
730 /* Find all threads that have their process
731 * handle==process_handle. Ignore the return value, all the
732 * work will be done in the compare func
734 (void)_wapi_search_handle (WAPI_HANDLE_THREAD, process_thread_compare,
735 process_handle, NULL, NULL);
737 unref_handle (daemon_channel_data,
738 GPOINTER_TO_UINT (process_handle_data->main_thread));
739 unref_handle (daemon_channel_data, GPOINTER_TO_UINT (process_handle));
742 static void process_died (void)
744 int status;
745 pid_t pid;
747 check_processes=FALSE;
749 LOGDEBUG ("%s: Reaping processes", __func__);
751 while(TRUE) {
752 pid=waitpid (-1, &status, WNOHANG);
753 if(pid==0 || pid==-1) {
754 /* Finished waiting. I was checking pid==-1
755 * separately but was getting ECHILD when
756 * there were no more child processes (which
757 * doesnt seem to conform to the man page)
759 return;
760 } else {
761 /* pid contains the ID of a dead process */
762 LOGDEBUG ( "%s: process %d reaped", __func__, pid);
763 process_post_mortem (pid, status);
770 * send_reply:
771 * @channel: channel to send reply to
772 * @resp: Package to send
774 * Send a package to a client
776 static void send_reply (GIOChannel *channel, WapiHandleResponse *resp)
778 /* send message */
779 _wapi_daemon_response (g_io_channel_unix_get_fd (channel), resp);
782 static guint32 new_handle_with_shared_check (WapiHandleType type)
784 guint32 handle = 0;
786 while ((handle = _wapi_handle_new_internal (type)) == 0) {
787 /* Try and allocate a new shared segment, and have
788 * another go
790 guint32 segment=_wapi_shared_data[0]->num_segments;
791 int i;
793 _wapi_handle_ensure_mapped (segment);
794 if(_wapi_shared_data[segment]!=NULL) {
795 /* Got a new segment */
796 gulong old_len, new_len;
798 old_len=_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT * sizeof(guint32);
799 _wapi_shared_data[0]->num_segments++;
800 new_len=_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT * sizeof(guint32);
802 /* Need to expand all the handle reference
803 * count arrays
806 for(i=0; i<channels_length; i++) {
807 if(channels[i].open_handles!=NULL) {
808 channels[i].open_handles=_wapi_g_renew0 (channels[i].open_handles, old_len, new_len);
811 } else {
812 /* Map failed. Just return 0 meaning "out of
813 * handles"
815 break;
819 return(handle);
823 * process_new:
824 * @channel: The client making the request
825 * @channel_data: Our data for this channel
826 * @type: type to init handle to
828 * Find a free handle and initialize it to 'type', increase refcnt and
829 * send back a reply to the client.
831 static void process_new (GIOChannel *channel, ChannelData *channel_data,
832 WapiHandleType type)
834 guint32 handle;
835 WapiHandleResponse resp={0};
837 handle = new_handle_with_shared_check (type);
839 /* handle might still be set to 0. This is handled at the
840 * client end
843 ref_handle (channel_data, handle);
845 LOGDEBUG ("%s: returning new handle 0x%x", __func__, handle);
847 resp.type=WapiHandleResponseType_New;
848 resp.u.new.type=type;
849 resp.u.new.handle=handle;
851 send_reply (channel, &resp);
855 * process_open:
856 * @channel: The client making the request
857 * @channel_data: Our data for this channel
858 * @handle: handle no.
860 * Increase refcnt on a previously created handle and send back a
861 * response to the client.
863 static void process_open (GIOChannel *channel, ChannelData *channel_data,
864 guint32 handle)
866 WapiHandleResponse resp={0};
867 guint32 segment, idx;
868 struct _WapiHandleShared *shared;
870 _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
871 shared=&_wapi_shared_data[segment]->handles[idx];
873 if(shared->type!=WAPI_HANDLE_UNUSED && handle!=0) {
874 ref_handle (channel_data, handle);
876 LOGDEBUG ("%s: returning new handle 0x%x", __func__, handle);
878 resp.type=WapiHandleResponseType_Open;
879 resp.u.new.type=shared->type;
880 resp.u.new.handle=handle;
882 send_reply (channel, &resp);
884 return;
887 resp.type=WapiHandleResponseType_Open;
888 resp.u.new.handle=0;
890 send_reply (channel, &resp);
894 * process_close:
895 * @channel: The client making the request
896 * @channel_data: Our data for this channel
897 * @handle: handle no.
899 * Decrease refcnt on a previously created handle and send back a
900 * response to the client with notice of it being destroyed.
902 static void process_close (GIOChannel *channel, ChannelData *channel_data,
903 guint32 handle)
905 WapiHandleResponse resp={0};
907 resp.type=WapiHandleResponseType_Close;
908 resp.u.close.destroy=unref_handle (channel_data, handle);
910 LOGDEBUG ("%s: unreffing handle 0x%x", __func__, handle);
912 send_reply (channel, &resp);
916 * process_scratch:
917 * @channel: The client making the request
918 * @length: allocate this much scratch space
920 * Allocate some scratch space and send a reply to the client.
922 static void process_scratch (GIOChannel *channel, guint32 length)
924 WapiHandleResponse resp={0};
926 resp.type=WapiHandleResponseType_Scratch;
927 resp.u.scratch.idx=_wapi_handle_scratch_store_internal (length, &resp.u.scratch.remap);
929 LOGDEBUG ("%s: allocating scratch index 0x%x", __func__, resp.u.scratch.idx);
931 send_reply (channel, &resp);
935 * process_scratch_free:
936 * @channel: The client making the request
937 * @scratch_idx: deallocate this scratch space
939 * Deallocate scratch space and send a reply to the client.
941 static void process_scratch_free (GIOChannel *channel, guint32 scratch_idx)
943 WapiHandleResponse resp={0};
945 resp.type=WapiHandleResponseType_ScratchFree;
946 _wapi_handle_scratch_delete_internal (scratch_idx);
948 LOGDEBUG ("%s: deleting scratch index 0x%x", __func__, scratch_idx);
950 send_reply (channel, &resp);
954 * process_process_kill:
955 * @channel: The client making the request
956 * @process_kill: pid and signal to send to the pid.
958 * Sends the specified signal to the process.
960 static void
961 process_process_kill (GIOChannel *channel,
962 WapiHandleRequest_ProcessKill process_kill)
964 WapiHandleResponse resp = {0};
966 resp.type = WapiHandleResponseType_ProcessKill;
968 LOGDEBUG ("%s: kill (%d, %d)", __func__, process_kill.pid, process_kill.signo);
970 if (kill (process_kill.pid, process_kill.signo) == -1) {
971 resp.u.process_kill.err = errno;
972 LOGDEBUG ("%s: kill (%d, %d) failed: %d", __func__, process_kill.pid, process_kill.signo, resp.u.process_kill.err);
975 send_reply (channel, &resp);
979 * process_process_fork:
980 * @channel: The client making the request
981 * @process_fork: Describes the process to fork
982 * @fds: stdin, stdout, and stderr for the new process
984 * Forks a new process, and returns the process and thread data to the
985 * client.
987 static void process_process_fork (GIOChannel *channel, ChannelData *channel_data,
988 WapiHandleRequest_ProcessFork process_fork,
989 int *fds)
991 WapiHandleResponse resp={0};
992 guint32 process_handle, thread_handle;
993 struct _WapiHandle_process *process_handle_data;
994 struct _WapiHandle_thread *thread_handle_data;
995 pid_t pid = 0;
997 resp.type=WapiHandleResponseType_ProcessFork;
999 /* Create handles first, so the child process can store exec
1000 * errors. Either handle might be set to 0, if this happens
1001 * just reply to the client without bothering to fork. The
1002 * client must check if either handle is 0 and take
1003 * appropriate error handling action.
1005 process_handle = new_handle_with_shared_check (WAPI_HANDLE_PROCESS);
1006 ref_handle (daemon_channel_data, process_handle);
1007 ref_handle (channel_data, process_handle);
1009 thread_handle = new_handle_with_shared_check (WAPI_HANDLE_THREAD);
1010 ref_handle (daemon_channel_data, thread_handle);
1011 ref_handle (channel_data, thread_handle);
1013 if(process_handle==0 || thread_handle==0) {
1014 /* unref_handle() copes with the handle being 0 */
1015 unref_handle (daemon_channel_data, process_handle);
1016 unref_handle (channel_data, process_handle);
1017 unref_handle (daemon_channel_data, thread_handle);
1018 unref_handle (channel_data, thread_handle);
1019 process_handle=0;
1020 thread_handle=0;
1021 } else {
1022 char *cmd=NULL, *dir=NULL, **argv, **env;
1023 GError *gerr=NULL;
1024 gboolean ret;
1025 struct timeval tv;
1027 /* Get usable copies of the cmd, dir and env now
1028 * rather than in the child process. This is to
1029 * prevent the race condition where the parent can
1030 * return the reply to the client, which then promptly
1031 * deletes the scratch data before the new process
1032 * gets to see it. Also explode argv here so we can
1033 * use it to set the process name.
1035 cmd=_wapi_handle_scratch_lookup (process_fork.cmd);
1036 dir=_wapi_handle_scratch_lookup (process_fork.dir);
1037 env=_wapi_handle_scratch_lookup_string_array (process_fork.env);
1039 _wapi_lookup_handle (GUINT_TO_POINTER (process_handle),
1040 WAPI_HANDLE_PROCESS,
1041 (gpointer *)&process_handle_data,
1042 NULL);
1044 _wapi_lookup_handle (GUINT_TO_POINTER (thread_handle),
1045 WAPI_HANDLE_THREAD,
1046 (gpointer *)&thread_handle_data,
1047 NULL);
1049 ret=g_shell_parse_argv (cmd, NULL, &argv, &gerr);
1050 if(ret==FALSE) {
1051 /* FIXME: Could do something with the
1052 * GError here
1054 process_handle_data->exec_errno=gerr->code;
1055 } else {
1056 LOGDEBUG ("%s: forking", __func__);
1058 /* Fork, exec cmd with args and optional env,
1059 * and return the handles with pid and blank
1060 * thread id
1062 pid=fork ();
1063 if(pid==-1) {
1064 process_handle_data->exec_errno=errno;
1065 } else if (pid==0) {
1066 /* child */
1067 int i;
1069 /* should we detach from the process
1070 * group? We're already running
1071 * without a controlling tty...
1074 /* Connect stdin, stdout and stderr */
1075 dup2 (fds[0], 0);
1076 dup2 (fds[1], 1);
1077 dup2 (fds[2], 2);
1079 if(process_fork.inherit!=TRUE) {
1080 /* FIXME: do something here */
1083 /* Close all file descriptors */
1084 for (i = getdtablesize () - 1; i > 2; i--) {
1085 close (i);
1088 /* pass process and thread handle info
1089 * to the child, so it doesn't have to
1090 * do an expensive search over the
1091 * whole list
1094 guint env_count=0;
1096 while(env[env_count]!=NULL) {
1097 env_count++;
1100 env=(char **)g_renew (char **, env, env_count+3);
1102 env[env_count]=g_strdup_printf ("_WAPI_PROCESS_HANDLE=%d", process_handle);
1103 env[env_count+1]=g_strdup_printf ("_WAPI_THREAD_HANDLE=%d", thread_handle);
1104 env[env_count+2]=NULL;
1107 #ifdef DEBUG
1108 LOGDEBUG ("%s: exec()ing [%s] in dir [%s]", __func__, cmd, dir);
1110 i=0;
1111 while(argv[i]!=NULL) {
1112 LOGDEBUG ("arg %d: [%s]", i, argv[i]);
1113 i++;
1116 i=0;
1117 while(env[i]!=NULL) {
1118 LOGDEBUG ("env %d: [%s]", i, env[i]);
1119 i++;
1122 #endif
1124 /* set cwd */
1125 if(chdir (dir)==-1) {
1126 process_handle_data->exec_errno=errno;
1127 exit (-1);
1130 /* exec */
1131 execve (argv[0], argv, env);
1133 /* bummer! */
1134 process_handle_data->exec_errno=errno;
1135 exit (-1);
1138 /* parent */
1140 /* store process name, based on the last section of the cmd */
1142 char *slash;
1144 /* This should never fail, but it seems it can...
1146 if (argv[0] != NULL) {
1147 slash=strrchr (argv[0], '/');
1149 if(slash!=NULL) {
1150 process_handle_data->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
1151 } else {
1152 process_handle_data->proc_name=_wapi_handle_scratch_store (argv[0], strlen (argv[0]));
1154 } else {
1155 process_handle_data->proc_name = _wapi_handle_scratch_store (cmd, strlen(cmd));
1159 /* These seem to be the defaults on w2k */
1160 process_handle_data->min_working_set=204800;
1161 process_handle_data->max_working_set=1413120;
1163 if(cmd!=NULL) {
1164 g_free (cmd);
1166 if(dir!=NULL) {
1167 g_free (dir);
1169 g_strfreev (argv);
1170 g_strfreev (env);
1172 /* store pid */
1173 process_handle_data->id=pid;
1174 process_handle_data->main_thread=GUINT_TO_POINTER (thread_handle);
1175 /* Ignore errors */
1176 gettimeofday (&tv, NULL);
1177 _wapi_timeval_to_filetime (&tv,
1178 &process_handle_data->create_time);
1180 /* FIXME: if env==0, inherit the env from the current
1181 * process
1183 process_handle_data->env=process_fork.env;
1185 thread_handle_data->process_handle=GUINT_TO_POINTER (process_handle);
1187 resp.u.process_fork.pid=pid;
1190 resp.u.process_fork.process_handle=process_handle;
1191 resp.u.process_fork.thread_handle=thread_handle;
1193 send_reply (channel, &resp);
1197 * process_set_share:
1198 * @channel: The client making the request
1199 * @channel_data: The channel data
1200 * @set_share: Set share data passed from the client
1202 * Sets file share info
1204 static void process_set_share (GIOChannel *channel, ChannelData *channel_data,
1205 WapiHandleRequest_SetShare set_share)
1207 WapiHandleResponse resp = {0};
1209 resp.type = WapiHandleResponseType_SetShare;
1211 LOGDEBUG ("%s: Setting share for file (dev:0x%llx, ino:%lld) mode 0x%x access 0x%x", __func__, set_share.device, set_share.inode, set_share.sharemode, set_share.access);
1213 sharemode_set (set_share.device, set_share.inode, set_share.sharemode,
1214 set_share.access);
1216 send_reply (channel, &resp);
1220 * process_get_or_set_share:
1221 * @channel: The client making the request
1222 * @channel_data: The channel data
1223 * @get_share: GetOrSetShare data passed from the client
1225 * Gets a file share status, and sets the status if it doesn't already
1226 * exist
1228 static void process_get_or_set_share (GIOChannel *channel,
1229 ChannelData *channel_data,
1230 WapiHandleRequest_GetOrSetShare get_share)
1232 WapiHandleResponse resp = {0};
1234 resp.type = WapiHandleResponseType_GetOrSetShare;
1236 LOGDEBUG ("%s: Getting share status for file (dev:0x%llx, ino:%lld)", __func__, get_share.device, get_share.inode);
1238 resp.u.get_or_set_share.exists = sharemode_get (get_share.device, get_share.inode, &resp.u.get_or_set_share.sharemode, &resp.u.get_or_set_share.access);
1240 if (resp.u.get_or_set_share.exists) {
1241 LOGDEBUG ("%s: Share mode: 0x%x", __func__, resp.u.get_or_set_share.sharemode);
1242 } else {
1243 LOGDEBUG ("%s: file share info not already known, setting", __func__);
1244 sharemode_set (get_share.device, get_share.inode,
1245 get_share.new_sharemode, get_share.new_access);
1248 send_reply (channel, &resp);
1252 * read_message:
1253 * @channel: The client to read the request from
1254 * @open_handles: An array of handles referenced by this client
1256 * Read a message (A WapiHandleRequest) from a client and dispatch
1257 * whatever it wants to the process_* calls. Return TRUE if the message
1258 * was read successfully, FALSE otherwise.
1260 static gboolean read_message (GIOChannel *channel, ChannelData *channel_data)
1262 WapiHandleRequest req;
1263 int fds[3]={0, 1, 2};
1264 int ret;
1265 gboolean has_fds=FALSE;
1267 /* Reading data */
1268 ret=_wapi_daemon_request (g_io_channel_unix_get_fd (channel), &req,
1269 fds, &has_fds);
1270 if(ret==0) {
1271 /* Other end went away */
1272 LOGDEBUG ("Read 0 bytes on fd %d, closing it",
1273 g_io_channel_unix_get_fd (channel));
1274 rem_fd (channel, channel_data);
1275 return(FALSE);
1278 LOGDEBUG ("Process request %d", req.type);
1279 switch(req.type) {
1280 case WapiHandleRequestType_New:
1281 process_new (channel, channel_data, req.u.new.type);
1282 break;
1283 case WapiHandleRequestType_Open:
1284 #ifdef DEBUG
1285 g_assert(req.u.open.handle < _wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
1286 #endif
1287 process_open (channel, channel_data, req.u.open.handle);
1288 break;
1289 case WapiHandleRequestType_Close:
1290 #ifdef DEBUG
1291 g_assert(req.u.close.handle < _wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
1292 #endif
1293 process_close (channel, channel_data, req.u.close.handle);
1294 break;
1295 case WapiHandleRequestType_Scratch:
1296 process_scratch (channel, req.u.scratch.length);
1297 break;
1298 case WapiHandleRequestType_ScratchFree:
1299 process_scratch_free (channel, req.u.scratch_free.idx);
1300 break;
1301 case WapiHandleRequestType_ProcessFork:
1302 process_process_fork (channel, channel_data,
1303 req.u.process_fork, fds);
1304 break;
1305 case WapiHandleRequestType_ProcessKill:
1306 process_process_kill (channel, req.u.process_kill);
1307 break;
1308 case WapiHandleRequestType_SetShare:
1309 process_set_share (channel, channel_data, req.u.set_share);
1310 break;
1311 case WapiHandleRequestType_GetOrSetShare:
1312 process_get_or_set_share (channel, channel_data,
1313 req.u.get_or_set_share);
1314 break;
1315 case WapiHandleRequestType_Error:
1316 /* fall through */
1317 default:
1318 /* Catch bogus requests */
1319 /* FIXME: call rem_fd? */
1320 break;
1323 if(has_fds==TRUE) {
1324 LOGDEBUG ("%s: closing %d", __func__, fds[0]);
1325 LOGDEBUG ("%s: closing %d", __func__, fds[1]);
1326 LOGDEBUG ("%s: closing %d", __func__, fds[2]);
1328 close (fds[0]);
1329 close (fds[1]);
1330 close (fds[2]);
1333 return(TRUE);
1337 * fd_activity:
1338 * @channel: The IO channel that is active
1339 * @condition: The condition that has been satisfied
1340 * @data: A pointer to an array of handles referenced by this client
1342 * The callback called by the main loop when there is activity on an
1343 * IO channel.
1345 static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
1346 gpointer data)
1348 int fd=g_io_channel_unix_get_fd (channel);
1349 ChannelData *channel_data=&channels[fd];
1350 GMainContext *context=data;
1352 if(condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
1353 LOGDEBUG ("fd %d error", fd);
1354 rem_fd (channel, channel_data);
1355 return(FALSE);
1358 if(condition & (_IO_PRI)) {
1359 if(fd==main_sock) {
1360 int newsock;
1361 struct sockaddr addr;
1362 socklen_t addrlen=sizeof(struct sockaddr);
1364 newsock=accept (main_sock, &addr, &addrlen);
1365 if(newsock==-1) {
1366 g_critical ("%s accept error: %s", __func__, g_strerror (errno));
1367 cleanup ();
1368 exit (-1);
1371 LOGDEBUG ("accept returning %d", newsock);
1372 add_fd (newsock, context);
1373 } else {
1374 LOGDEBUG ("reading data on fd %d", fd);
1376 return(read_message (channel, channel_data));
1378 return(TRUE);
1381 return(FALSE); /* remove source */
1385 * _wapi_daemon_main:
1387 * Open socket, create shared mem segment and begin listening for
1388 * clients.
1390 void _wapi_daemon_main(gpointer data, gpointer scratch)
1392 struct sockaddr_un main_socket_address;
1393 int ret;
1394 GMainContext *context;
1396 LOGDEBUG ("Starting up...");
1398 _wapi_shared_data[0]=data;
1399 _wapi_shared_scratch=scratch;
1400 _wapi_shared_scratch->is_shared=TRUE;
1402 /* Note that we've got the starting segment already */
1403 _wapi_shared_data[0]->num_segments=1;
1404 _wapi_shm_mapped_segments=1;
1406 _wapi_fd_offset_table_size=getdtablesize ();
1407 _wapi_shared_data[0]->fd_offset_table_size = _wapi_fd_offset_table_size;
1409 startup ();
1411 main_sock=socket(PF_UNIX, SOCK_STREAM, 0);
1413 main_socket_address.sun_family=AF_UNIX;
1414 memcpy(main_socket_address.sun_path, _wapi_shared_data[0]->daemon,
1415 MONO_SIZEOF_SUNPATH);
1417 ret=bind(main_sock, (struct sockaddr *)&main_socket_address,
1418 sizeof(struct sockaddr_un));
1419 if(ret==-1) {
1420 g_critical ("bind failed: %s", g_strerror (errno));
1421 _wapi_shared_data[0]->daemon_running=DAEMON_DIED_AT_STARTUP;
1422 exit(-1);
1425 LOGDEBUG("bound");
1427 ret=listen(main_sock, 5);
1428 if(ret==-1) {
1429 g_critical ("listen failed: %s", g_strerror (errno));
1430 _wapi_shared_data[0]->daemon_running=DAEMON_DIED_AT_STARTUP;
1431 exit(-1);
1433 LOGDEBUG("listening");
1435 context = g_main_context_new ();
1437 add_fd(main_sock, context);
1439 /* We're finished setting up, let everyone else know we're
1440 * ready. From now on, it's up to us to delete the shared
1441 * memory segment when appropriate.
1443 _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING;
1445 while(TRUE) {
1446 if(check_processes==TRUE) {
1447 process_died ();
1450 LOGDEBUG ("polling");
1452 /* Block until something happens. We don't use
1453 * g_main_loop_run() because we rely on the SIGCHLD
1454 * signal interrupting poll() so we can reap child
1455 * processes as soon as they die, without burning cpu
1456 * time by polling the flag.
1458 g_main_context_iteration (context, TRUE);