2009-05-15 Geoff Norton <gnorton@novell.com>
[mono-project.git] / mono / io-layer / daemon.c
blob0f5ad38ab882c7136f58c7e02e21ca1e4594c979
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 #undef DEBUG
39 /* The shared thread codepath doesn't seem to work yet... */
40 #undef _POSIX_THREAD_PROCESS_SHARED
42 /* Keep track of the number of clients */
43 static int nfds=0;
45 /* Arrays to keep track of channel data for the
46 * daemon and clients indexed by file descriptor
47 * value.
50 typedef struct _channel_data {
51 int io_source; /* the ID given back by g_io_add_watch */
52 guint32 *open_handles; /* array of open handles for this client */
53 } ChannelData;
55 static ChannelData *daemon_channel_data=NULL;
56 static ChannelData *channels=NULL;
57 static int channels_length=0;
59 /* The socket which we listen to new connections on */
60 static int main_sock;
62 /* Set to TRUE by the SIGCHLD signal handler */
63 static volatile gboolean check_processes=FALSE;
65 /* The file_share_hash is used to emulate the windows file sharing mode */
66 typedef struct _share_key
68 dev_t device;
69 ino_t inode;
70 } ShareKey;
72 typedef struct _share_data
74 guint32 sharemode;
75 guint32 access;
76 } ShareData;
78 static GHashTable *file_share_hash = NULL;
80 static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
81 gpointer data);
82 static void check_sharing (dev_t device, ino_t inode);
84 /* Deletes the shared memory segment. If we're exiting on error,
85 * clients will get EPIPEs.
87 static void cleanup (void)
89 int i;
91 #ifdef NEED_LINK_UNLINK
92 unlink(_wapi_shared_data[0]->daemon);
93 #endif
94 for(i=1; i<_wapi_shared_data[0]->num_segments; i++) {
95 unlink (_wapi_shm_file (WAPI_SHM_DATA, i));
97 unlink (_wapi_shm_file (WAPI_SHM_DATA, 0));
99 /* There's only one scratch file */
100 unlink (_wapi_shm_file (WAPI_SHM_SCRATCH, 0));
103 /* If there is only one socket, and no child processes, we can exit.
104 * We test for child processes by counting handle references held by
105 * the daemon.
107 static void maybe_exit (void)
109 guint32 i;
111 #ifdef DEBUG
112 g_message (G_GNUC_PRETTY_FUNCTION ": Seeing if we should exit");
113 #endif
115 if(nfds>1) {
116 #ifdef DEBUG
117 g_message (G_GNUC_PRETTY_FUNCTION ": Still got clients");
118 #endif
119 return;
122 /* Prevent new clients from connecting... */
123 _wapi_shared_data[0]->daemon_running=DAEMON_CLOSING;
125 for(i=0;
126 i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT;
127 i++) {
128 if(daemon_channel_data->open_handles[i]>0) {
129 #ifdef DEBUG
130 g_message (G_GNUC_PRETTY_FUNCTION
131 ": Still got handle references");
132 #endif
134 _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING;
135 return;
139 #ifdef HAVE_POLL
140 /* Last check, make sure no client came along while we were
141 * checking the handle lists.
143 * Use poll() directly here, as glib doesn't seem to have any
144 * exposed way of seeing if a file descriptor is ready
145 * (g_io_channel_get_buffer_condition() isn't it.)
147 * Crappy systems that don't have poll() will just have to
148 * lump it (for them there is still the very slight chance
149 * that someone tried to connect just as the DAEMON_CLOSING
150 * flag was being set.)
153 struct pollfd fds[1];
155 fds[0].fd=main_sock;
156 fds[0].events=POLLIN;
157 fds[0].revents=0;
159 #ifdef DEBUG
160 g_message (G_GNUC_PRETTY_FUNCTION ": Last connect check");
161 #endif
163 if(poll (fds, 1, 0)>0) {
164 /* Someone did connect, so carry on running */
165 #ifdef DEBUG
166 g_message (G_GNUC_PRETTY_FUNCTION
167 ": Someone connected");
168 #endif
170 _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING;
171 return;
174 #endif
176 #ifdef DEBUG
177 g_message (G_GNUC_PRETTY_FUNCTION ": Byebye");
178 #endif
180 cleanup ();
181 exit (0);
185 * signal_handler:
186 * @unused: unused
188 * Called if daemon receives a SIGTERM or SIGINT
190 static void signal_handler (int signo)
192 #ifdef DEBUG
193 g_message (G_GNUC_PRETTY_FUNCTION ": daemon received signal %d", signo);
194 #endif
195 cleanup ();
196 exit (-1);
200 * sigchld_handler:
201 * @unused: unused
203 * Called if daemon receives a SIGCHLD, and notes that a process needs
204 * to be wait()ed for.
206 static void sigchld_handler (int unused)
208 /* Notice that a child process died */
209 check_processes=TRUE;
212 static guint sharedata_hash (gconstpointer key)
214 ShareKey *sharekey = (ShareKey *)key;
216 return(g_int_hash (&(sharekey->inode)));
219 static gboolean sharedata_equal (gconstpointer a, gconstpointer b)
221 ShareKey *share_a = (ShareKey *)a;
222 ShareKey *share_b = (ShareKey *)b;
224 return(share_a->device == share_b->device &&
225 share_a->inode == share_b->inode);
229 * startup:
231 * Bind signals, attach to shared memory and set up any internal data
232 * structures needed.
234 static void startup (void)
236 struct sigaction sa;
238 sa.sa_handler=signal_handler;
239 sigemptyset (&sa.sa_mask);
240 sa.sa_flags=0;
241 sigaction (SIGINT, &sa, NULL);
242 sigaction (SIGTERM, &sa, NULL);
244 #ifndef HAVE_MSG_NOSIGNAL
245 sa.sa_handler=SIG_IGN;
246 sigaction (SIGPIPE, &sa, NULL);
247 #endif
249 sa.sa_handler=sigchld_handler;
250 sa.sa_flags=SA_NOCLDSTOP;
251 sigaction (SIGCHLD, &sa, NULL);
253 #ifdef NEED_LINK_UNLINK
254 /* Here's a more portable method... */
255 snprintf (_wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH-1,
256 "/tmp/mono-handle-daemon-%d-%ld-%ld", getuid (), random (),
257 time (NULL));
258 #else
259 /* Leave the first byte NULL so we create the socket in the
260 * abstrace namespace, not on the filesystem. (Lets see how
261 * portable _that_ is :)
263 * The name is intended to be unique, not cryptographically
264 * secure...
266 snprintf (_wapi_shared_data[0]->daemon+1, MONO_SIZEOF_SUNPATH-2,
267 "mono-handle-daemon-%d-%d-%ld", getuid (), getpid (),
268 time (NULL));
269 #endif
271 file_share_hash = g_hash_table_new_full (sharedata_hash,
272 sharedata_equal, g_free,
273 g_free);
278 * ref_handle:
279 * @channel_data: Channel data for calling client
280 * @handle: handle to inc refcnt
282 * Increase ref count of handle for the calling client. Handle 0 is
283 * ignored.
285 static void ref_handle (ChannelData *channel_data, guint32 handle)
287 guint32 segment, idx;
289 if(handle==0) {
290 return;
293 _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
295 _wapi_shared_data[segment]->handles[idx].ref++;
296 channel_data->open_handles[handle]++;
298 #ifdef DEBUG
299 g_message (G_GNUC_PRETTY_FUNCTION
300 ": handle 0x%x ref now %d (%d this process)", handle,
301 _wapi_shared_data[segment]->handles[idx].ref,
302 channel_data->open_handles[handle]);
303 #endif
307 * unref_handle:
308 * @channel_data: Channel data for calling client
309 * @handle: handle to inc refcnt
311 * Decrease ref count of handle for the calling client. If global ref
312 * count reaches 0 it is free'ed. Return TRUE if the local ref count
313 * is 0. Handle 0 is ignored.
315 static gboolean unref_handle (ChannelData *channel_data, guint32 handle)
317 gboolean destroy=FALSE;
318 guint32 segment, idx;
320 if(handle==0) {
321 return(FALSE);
324 if (channel_data->open_handles[handle] == 0) {
325 g_warning(G_GNUC_PRETTY_FUNCTION
326 ": unref on %d called when ref was already 0",
327 handle);
328 return TRUE;
331 _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
333 _wapi_shared_data[segment]->handles[idx].ref--;
334 channel_data->open_handles[handle]--;
336 #ifdef DEBUG
337 g_message (G_GNUC_PRETTY_FUNCTION
338 ": handle 0x%x ref now %d (%d this process)", handle,
339 _wapi_shared_data[segment]->handles[idx].ref,
340 channel_data->open_handles[handle]);
341 #endif
343 if (_wapi_shared_data[segment]->handles[idx].ref == 0) {
344 gboolean was_file;
345 dev_t device = 0;
346 ino_t inode = 0;
348 /* This client has released the handle */
349 destroy=TRUE;
351 if (channel_data->open_handles[handle]!=0) {
352 g_warning (G_GNUC_PRETTY_FUNCTION ": per-process open_handles mismatch, set to %d, should be 0",
353 channel_data->open_handles[handle]);
356 #ifdef DEBUG
357 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle 0x%x",
358 handle);
359 #endif
361 /* if this was a file handle, save the device and
362 * inode numbers so we can scan the share info data
363 * later to see if the last handle to a file has been
364 * closed, and delete the data if so.
366 was_file = (_wapi_shared_data[segment]->handles[idx].type == WAPI_HANDLE_FILE);
367 if (was_file) {
368 struct _WapiHandle_file *file_handle;
369 gboolean ok;
371 ok = _wapi_lookup_handle (GUINT_TO_POINTER (handle),
372 WAPI_HANDLE_FILE,
373 (gpointer *)&file_handle,
374 NULL);
375 if (ok == FALSE) {
376 g_warning (G_GNUC_PRETTY_FUNCTION
377 ": error looking up file handle %x",
378 handle);
379 } else {
380 device = file_handle->device;
381 inode = file_handle->inode;
385 _wapi_handle_ops_close_shared (GUINT_TO_POINTER (handle));
387 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
388 mono_mutex_destroy (&_wapi_shared_data[segment]->handles[idx].signal_mutex);
389 pthread_cond_destroy (&_wapi_shared_data[segment]->handles[idx].signal_cond);
390 #endif
392 memset (&_wapi_shared_data[segment]->handles[idx].u, '\0', sizeof(_wapi_shared_data[segment]->handles[idx].u));
393 _wapi_shared_data[segment]->handles[idx].type=WAPI_HANDLE_UNUSED;
395 if (was_file) {
396 check_sharing (device, inode);
400 if(channel_data == daemon_channel_data) {
401 /* The daemon released a reference, so see if it's
402 * ready to exit
404 maybe_exit ();
407 return(destroy);
411 * add_fd:
412 * @fd: Filehandle to add
414 * Create a new GIOChannel, and add it to the main loop event sources.
416 static void add_fd(int fd, GMainContext *context)
418 GIOChannel *io_channel;
419 GSource *source;
420 guint32 *refs;
422 io_channel=g_io_channel_unix_new (fd);
424 /* Turn off all encoding and buffering crap */
425 g_io_channel_set_encoding (io_channel, NULL, NULL);
426 g_io_channel_set_buffered (io_channel, FALSE);
428 refs=g_new0 (guint32,_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
430 if(fd>=channels_length) {
431 /* Add a bit of padding, so we dont resize for _every_
432 * new connection
434 int old_len=channels_length * sizeof(ChannelData);
436 channels_length=fd+10;
437 if(channels==NULL) {
438 channels=g_new0 (ChannelData, channels_length);
439 /* We rely on the daemon channel being created first.
440 * That's safe, because every other channel is the
441 * result of an accept() on the daemon channel.
443 daemon_channel_data = &channels[fd];
444 } else {
445 int daemon_index=daemon_channel_data - channels;
447 /* Can't use g_renew here, because the unused
448 * elements must be NULL and g_renew doesn't
449 * initialise the memory it returns
451 channels=_wapi_g_renew0 (channels, old_len, channels_length * sizeof(ChannelData));
452 daemon_channel_data = channels + daemon_index;
457 channels[fd].open_handles=refs;
459 source = g_io_create_watch (io_channel,
460 G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
461 g_source_set_callback (source, (GSourceFunc)fd_activity,
462 context, NULL);
463 channels[fd].io_source=g_source_attach (source, context);
464 g_source_unref (source);
466 nfds++;
470 * rem_fd:
471 * @channel: GIOChannel to close
473 * Closes the IO channel. Closes all handles that it may have open. If
474 * only main_sock is left, the daemon is shut down.
476 static void rem_fd(GIOChannel *channel, ChannelData *channel_data)
478 guint32 handle_count;
479 int i, j, fd;
481 fd=g_io_channel_unix_get_fd (channel);
483 if(fd == main_sock) {
484 /* We shouldn't be deleting the daemon's fd */
485 g_warning (G_GNUC_PRETTY_FUNCTION ": Deleting daemon fd!");
486 cleanup ();
487 exit (-1);
490 #ifdef DEBUG
491 g_message (G_GNUC_PRETTY_FUNCTION ": Removing client fd %d", fd);
492 #endif
494 if (channel_data->io_source == 0) {
495 #ifdef DEBUG
496 g_message (G_GNUC_PRETTY_FUNCTION ": channel already closed for fd %d", fd);
497 #endif
498 return;
502 g_io_channel_shutdown (channel, TRUE, NULL);
503 g_source_remove (channel_data->io_source);
504 g_io_channel_unref (channel);
506 for(i=0;
507 i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT;
508 i++) {
509 handle_count=channel_data->open_handles[i];
511 for(j=0; j<handle_count; j++) {
512 #ifdef DEBUG
513 g_message (G_GNUC_PRETTY_FUNCTION ": closing handle 0x%x for client at index %d", i, g_io_channel_unix_get_fd (channel));
514 #endif
515 /* Ignore the hint to the client to destroy
516 * the handle private data
518 unref_handle (channel_data, i);
522 g_free (channel_data->open_handles);
523 channel_data->open_handles=NULL;
524 channel_data->io_source=0;
526 nfds--;
527 if(nfds==1) {
528 /* Just the master socket left, so see if we can
529 * cleanup and exit
531 maybe_exit ();
535 static void sharemode_set (dev_t device, ino_t inode, guint32 sharemode,
536 guint32 access)
538 ShareKey *sharekey;
539 ShareData *sharedata;
541 sharekey = g_new (ShareKey, 1);
542 sharekey->device = device;
543 sharekey->inode = inode;
545 sharedata = g_new (ShareData, 1);
546 sharedata->sharemode = sharemode;
547 sharedata->access = access;
549 /* Setting share mode to include all access bits is really
550 * removing the share info
552 if (sharemode == (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)) {
553 g_hash_table_remove (file_share_hash, sharekey);
554 } else {
555 g_hash_table_insert (file_share_hash, sharekey, sharedata);
559 static gboolean sharemode_get (dev_t device, ino_t inode, guint32 *sharemode,
560 guint32 *access)
562 ShareKey sharekey;
563 ShareData *sharedata;
565 sharekey.device = device;
566 sharekey.inode = inode;
568 sharedata = (ShareData *)g_hash_table_lookup (file_share_hash,
569 &sharekey);
570 if (sharedata == NULL) {
571 return(FALSE);
574 *sharemode = sharedata->sharemode;
575 *access = sharedata->access;
577 return(TRUE);
580 static gboolean share_compare (gpointer handle, gpointer user_data)
582 struct _WapiHandle_file *file_handle;
583 gboolean ok;
584 ShareKey *sharekey = (ShareKey *)user_data;
586 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
587 (gpointer *)&file_handle, NULL);
588 if (ok == FALSE) {
589 g_warning (G_GNUC_PRETTY_FUNCTION
590 ": error looking up file handle %p", handle);
591 return(FALSE);
594 if (file_handle->device == sharekey->device &&
595 file_handle->inode == sharekey->inode) {
596 #ifdef DEBUG
597 g_message (G_GNUC_PRETTY_FUNCTION ": found one, handle %p",
598 handle);
599 #endif
600 return(TRUE);
601 } else {
602 return(FALSE);
606 static void check_sharing (dev_t device, ino_t inode)
608 ShareKey sharekey;
609 gpointer file_handle;
611 #ifdef DEBUG
612 g_message (G_GNUC_PRETTY_FUNCTION ": Checking if anything has (dev 0x%llx, inode %lld) still open", device, inode);
613 #endif
615 sharekey.device = device;
616 sharekey.inode = inode;
618 file_handle = _wapi_search_handle (WAPI_HANDLE_FILE, share_compare,
619 &sharekey, NULL, NULL);
621 if (file_handle == NULL) {
622 /* Delete this share info, as the last handle to it
623 * has been closed
625 #ifdef DEBUG
626 g_message (G_GNUC_PRETTY_FUNCTION ": Deleting share data for (dev 0x%llx inode %lld)", device, inode);
627 #endif
629 g_hash_table_remove (file_share_hash, &sharekey);
633 static gboolean process_compare (gpointer handle, gpointer user_data)
635 struct _WapiHandle_process *process_handle;
636 gboolean ok;
637 pid_t pid;
638 guint32 segment, idx;
640 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
641 (gpointer *)&process_handle, NULL);
642 if(ok==FALSE) {
643 g_warning (G_GNUC_PRETTY_FUNCTION
644 ": error looking up process handle %p", handle);
645 return(FALSE);
648 _wapi_handle_segment (handle, &segment, &idx);
649 if (_wapi_shared_data[segment]->handles[idx].signalled) {
650 return(FALSE);
653 pid=GPOINTER_TO_UINT (user_data);
654 if(process_handle->id==pid) {
655 return(TRUE);
656 } else {
657 return(FALSE);
661 static gboolean process_thread_compare (gpointer handle, gpointer user_data)
663 struct _WapiHandle_thread *thread_handle;
664 gboolean ok;
665 guint32 segment, idx;
667 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
668 (gpointer *)&thread_handle, NULL);
669 if(ok==FALSE) {
670 g_warning (G_GNUC_PRETTY_FUNCTION
671 ": error looking up thread handle %p", handle);
672 return(FALSE);
675 if(thread_handle->process_handle==user_data) {
676 /* Signal the handle. Don't use
677 * _wapi_handle_set_signal_state() unless we have
678 * process-shared pthread support.
680 #ifdef DEBUG
681 g_message (G_GNUC_PRETTY_FUNCTION ": Set thread handle %p signalled, because its process died", handle);
682 #endif
684 thread_handle->exitstatus=0;
686 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
687 _wapi_handle_lock_handle (handle);
688 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
689 _wapi_handle_unlock_handle (handle);
690 #else
691 /* Just tweak the signal state directly. This is not
692 * recommended behaviour, but it works for threads
693 * because they can never become unsignalled. There
694 * are some nasty kludges in the handle waiting code
695 * to cope with missing condition signals for when
696 * process-shared pthread support is missing.
698 _wapi_handle_segment (handle, &segment, &idx);
699 _wapi_shared_data[segment]->handles[idx].signalled=TRUE;
700 #endif /* _POSIX_THREAD_PROCESS_SHARED */
703 /* Return false to keep searching */
704 return(FALSE);
707 /* Find the handle associated with pid, mark it dead and record exit
708 * status. Finds all thread handles associated with this process
709 * handle, and marks those signalled too, with exitstatus '0'. It
710 * also drops the daemon's reference to the handle, and the thread
711 * pointed at by main_thread.
713 static void process_post_mortem (pid_t pid, int status)
715 gpointer process_handle;
716 struct _WapiHandle_process *process_handle_data;
717 guint32 segment, idx;
719 process_handle=_wapi_search_handle (WAPI_HANDLE_PROCESS,
720 process_compare,
721 GUINT_TO_POINTER (pid),
722 (gpointer *)&process_handle_data,
723 NULL);
724 if(process_handle==0) {
725 #ifdef DEBUG
727 * This may happen if we use Process.EnableRaisingEvents +
728 * process.Exited event and the parent has finished.
730 g_warning (G_GNUC_PRETTY_FUNCTION
731 ": Couldn't find handle for process %d!", pid);
732 #endif
733 } else {
734 /* Signal the handle. Don't use
735 * _wapi_handle_set_signal_state() unless we have
736 * process-shared pthread support.
738 struct timeval tv;
740 #ifdef DEBUG
741 g_message (G_GNUC_PRETTY_FUNCTION
742 ": Set process %d exitstatus to %d", pid,
743 WEXITSTATUS (status));
744 #endif
746 /* If the child terminated due to the receipt of a signal,
747 * the exit status must be based on WTERMSIG, since WEXITSTATUS
748 * returns 0 in this case.
750 if (WIFSIGNALED(status))
751 process_handle_data->exitstatus=128 + WTERMSIG (status);
752 else
753 process_handle_data->exitstatus=WEXITSTATUS (status);
755 /* Ignore errors */
756 gettimeofday (&tv, NULL);
757 _wapi_timeval_to_filetime (&tv,
758 &process_handle_data->exit_time);
760 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
761 _wapi_handle_lock_handle (process_handle);
762 _wapi_handle_set_signal_state (process_handle, TRUE, TRUE);
763 _wapi_handle_unlock_handle (process_handle);
764 #else
765 /* Just tweak the signal state directly. This is not
766 * recommended behaviour, but it works for processes
767 * because they can never become unsignalled. There
768 * are some nasty kludges in the handle waiting code
769 * to cope with missing condition signals for when
770 * process-shared pthread support is missing.
772 _wapi_handle_segment (process_handle, &segment, &idx);
773 _wapi_shared_data[segment]->handles[idx].signalled=TRUE;
774 #endif /* _POSIX_THREAD_PROCESS_SHARED */
777 /* Find all threads that have their process
778 * handle==process_handle. Ignore the return value, all the
779 * work will be done in the compare func
781 (void)_wapi_search_handle (WAPI_HANDLE_THREAD, process_thread_compare,
782 process_handle, NULL, NULL);
784 unref_handle (daemon_channel_data,
785 GPOINTER_TO_UINT (process_handle_data->main_thread));
786 unref_handle (daemon_channel_data, GPOINTER_TO_UINT (process_handle));
789 static void process_died (void)
791 int status;
792 pid_t pid;
794 check_processes=FALSE;
796 #ifdef DEBUG
797 g_message (G_GNUC_PRETTY_FUNCTION ": Reaping processes");
798 #endif
800 while(TRUE) {
801 pid=waitpid (-1, &status, WNOHANG);
802 if(pid==0 || pid==-1) {
803 /* Finished waiting. I was checking pid==-1
804 * separately but was getting ECHILD when
805 * there were no more child processes (which
806 * doesnt seem to conform to the man page)
808 return;
809 } else {
810 /* pid contains the ID of a dead process */
811 #ifdef DEBUG
812 g_message (G_GNUC_PRETTY_FUNCTION ": process %d reaped", pid);
813 #endif
814 process_post_mortem (pid, status);
821 * send_reply:
822 * @channel: channel to send reply to
823 * @resp: Package to send
825 * Send a package to a client
827 static void send_reply (GIOChannel *channel, WapiHandleResponse *resp)
829 /* send message */
830 _wapi_daemon_response (g_io_channel_unix_get_fd (channel), resp);
833 static guint32 new_handle_with_shared_check (WapiHandleType type)
835 guint32 handle = 0;
837 while ((handle = _wapi_handle_new_internal (type)) == 0) {
838 /* Try and allocate a new shared segment, and have
839 * another go
841 guint32 segment=_wapi_shared_data[0]->num_segments;
842 int i;
844 _wapi_handle_ensure_mapped (segment);
845 if(_wapi_shared_data[segment]!=NULL) {
846 /* Got a new segment */
847 gulong old_len, new_len;
849 old_len=_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT * sizeof(guint32);
850 _wapi_shared_data[0]->num_segments++;
851 new_len=_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT * sizeof(guint32);
853 /* Need to expand all the handle reference
854 * count arrays
857 for(i=0; i<channels_length; i++) {
858 if(channels[i].open_handles!=NULL) {
859 channels[i].open_handles=_wapi_g_renew0 (channels[i].open_handles, old_len, new_len);
862 } else {
863 /* Map failed. Just return 0 meaning "out of
864 * handles"
866 break;
870 return(handle);
874 * process_new:
875 * @channel: The client making the request
876 * @channel_data: Our data for this channel
877 * @type: type to init handle to
879 * Find a free handle and initialize it to 'type', increase refcnt and
880 * send back a reply to the client.
882 static void process_new (GIOChannel *channel, ChannelData *channel_data,
883 WapiHandleType type)
885 guint32 handle;
886 WapiHandleResponse resp={0};
888 handle = new_handle_with_shared_check (type);
890 /* handle might still be set to 0. This is handled at the
891 * client end
894 ref_handle (channel_data, handle);
896 #ifdef DEBUG
897 g_message (G_GNUC_PRETTY_FUNCTION ": returning new handle 0x%x",
898 handle);
899 #endif
901 resp.type=WapiHandleResponseType_New;
902 resp.u.new.type=type;
903 resp.u.new.handle=handle;
905 send_reply (channel, &resp);
909 * process_open:
910 * @channel: The client making the request
911 * @channel_data: Our data for this channel
912 * @handle: handle no.
914 * Increase refcnt on a previously created handle and send back a
915 * response to the client.
917 static void process_open (GIOChannel *channel, ChannelData *channel_data,
918 guint32 handle)
920 WapiHandleResponse resp={0};
921 guint32 segment, idx;
922 struct _WapiHandleShared *shared;
924 _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
925 shared=&_wapi_shared_data[segment]->handles[idx];
927 if(shared->type!=WAPI_HANDLE_UNUSED && handle!=0) {
928 ref_handle (channel_data, handle);
930 #ifdef DEBUG
931 g_message (G_GNUC_PRETTY_FUNCTION
932 ": returning new handle 0x%x", handle);
933 #endif
935 resp.type=WapiHandleResponseType_Open;
936 resp.u.new.type=shared->type;
937 resp.u.new.handle=handle;
939 send_reply (channel, &resp);
941 return;
944 resp.type=WapiHandleResponseType_Open;
945 resp.u.new.handle=0;
947 send_reply (channel, &resp);
951 * process_close:
952 * @channel: The client making the request
953 * @channel_data: Our data for this channel
954 * @handle: handle no.
956 * Decrease refcnt on a previously created handle and send back a
957 * response to the client with notice of it being destroyed.
959 static void process_close (GIOChannel *channel, ChannelData *channel_data,
960 guint32 handle)
962 WapiHandleResponse resp={0};
964 resp.type=WapiHandleResponseType_Close;
965 resp.u.close.destroy=unref_handle (channel_data, handle);
967 #ifdef DEBUG
968 g_message (G_GNUC_PRETTY_FUNCTION ": unreffing handle 0x%x", handle);
969 #endif
971 send_reply (channel, &resp);
975 * process_scratch:
976 * @channel: The client making the request
977 * @length: allocate this much scratch space
979 * Allocate some scratch space and send a reply to the client.
981 static void process_scratch (GIOChannel *channel, guint32 length)
983 WapiHandleResponse resp={0};
985 resp.type=WapiHandleResponseType_Scratch;
986 resp.u.scratch.idx=_wapi_handle_scratch_store_internal (length, &resp.u.scratch.remap);
987 #ifdef DEBUG
988 g_message (G_GNUC_PRETTY_FUNCTION ": allocating scratch index 0x%x",
989 resp.u.scratch.idx);
990 #endif
992 send_reply (channel, &resp);
996 * process_scratch_free:
997 * @channel: The client making the request
998 * @scratch_idx: deallocate this scratch space
1000 * Deallocate scratch space and send a reply to the client.
1002 static void process_scratch_free (GIOChannel *channel, guint32 scratch_idx)
1004 WapiHandleResponse resp={0};
1006 resp.type=WapiHandleResponseType_ScratchFree;
1007 _wapi_handle_scratch_delete_internal (scratch_idx);
1009 #ifdef DEBUG
1010 g_message (G_GNUC_PRETTY_FUNCTION ": deleting scratch index 0x%x",
1011 scratch_idx);
1012 #endif
1014 send_reply (channel, &resp);
1018 * process_process_kill:
1019 * @channel: The client making the request
1020 * @process_kill: pid and signal to send to the pid.
1022 * Sends the specified signal to the process.
1024 static void
1025 process_process_kill (GIOChannel *channel,
1026 WapiHandleRequest_ProcessKill process_kill)
1028 WapiHandleResponse resp = {0};
1030 resp.type = WapiHandleResponseType_ProcessKill;
1032 #ifdef DEBUG
1033 g_message (G_GNUC_PRETTY_FUNCTION ": kill (%d, %d)",
1034 process_kill.pid, process_kill.signo);
1035 #endif
1036 if (kill (process_kill.pid, process_kill.signo) == -1) {
1037 resp.u.process_kill.err = errno;
1038 #ifdef DEBUG
1039 g_message (G_GNUC_PRETTY_FUNCTION ": kill (%d, %d) failed: %d",
1040 process_kill.pid, process_kill.signo, resp.u.process_kill.err);
1041 #endif
1044 send_reply (channel, &resp);
1048 * process_process_fork:
1049 * @channel: The client making the request
1050 * @process_fork: Describes the process to fork
1051 * @fds: stdin, stdout, and stderr for the new process
1053 * Forks a new process, and returns the process and thread data to the
1054 * client.
1056 static void process_process_fork (GIOChannel *channel, ChannelData *channel_data,
1057 WapiHandleRequest_ProcessFork process_fork,
1058 int *fds)
1060 WapiHandleResponse resp={0};
1061 guint32 process_handle, thread_handle;
1062 struct _WapiHandle_process *process_handle_data;
1063 struct _WapiHandle_thread *thread_handle_data;
1064 pid_t pid = 0;
1066 resp.type=WapiHandleResponseType_ProcessFork;
1068 /* Create handles first, so the child process can store exec
1069 * errors. Either handle might be set to 0, if this happens
1070 * just reply to the client without bothering to fork. The
1071 * client must check if either handle is 0 and take
1072 * appropriate error handling action.
1074 process_handle = new_handle_with_shared_check (WAPI_HANDLE_PROCESS);
1075 ref_handle (daemon_channel_data, process_handle);
1076 ref_handle (channel_data, process_handle);
1078 thread_handle = new_handle_with_shared_check (WAPI_HANDLE_THREAD);
1079 ref_handle (daemon_channel_data, thread_handle);
1080 ref_handle (channel_data, thread_handle);
1082 if(process_handle==0 || thread_handle==0) {
1083 /* unref_handle() copes with the handle being 0 */
1084 unref_handle (daemon_channel_data, process_handle);
1085 unref_handle (channel_data, process_handle);
1086 unref_handle (daemon_channel_data, thread_handle);
1087 unref_handle (channel_data, thread_handle);
1088 process_handle=0;
1089 thread_handle=0;
1090 } else {
1091 char *cmd=NULL, *dir=NULL, **argv, **env;
1092 GError *gerr=NULL;
1093 gboolean ret;
1094 struct timeval tv;
1096 /* Get usable copies of the cmd, dir and env now
1097 * rather than in the child process. This is to
1098 * prevent the race condition where the parent can
1099 * return the reply to the client, which then promptly
1100 * deletes the scratch data before the new process
1101 * gets to see it. Also explode argv here so we can
1102 * use it to set the process name.
1104 cmd=_wapi_handle_scratch_lookup (process_fork.cmd);
1105 dir=_wapi_handle_scratch_lookup (process_fork.dir);
1106 env=_wapi_handle_scratch_lookup_string_array (process_fork.env);
1108 _wapi_lookup_handle (GUINT_TO_POINTER (process_handle),
1109 WAPI_HANDLE_PROCESS,
1110 (gpointer *)&process_handle_data,
1111 NULL);
1113 _wapi_lookup_handle (GUINT_TO_POINTER (thread_handle),
1114 WAPI_HANDLE_THREAD,
1115 (gpointer *)&thread_handle_data,
1116 NULL);
1118 ret=g_shell_parse_argv (cmd, NULL, &argv, &gerr);
1119 if(ret==FALSE) {
1120 /* FIXME: Could do something with the
1121 * GError here
1123 process_handle_data->exec_errno=gerr->code;
1124 } else {
1125 #ifdef DEBUG
1126 g_message (G_GNUC_PRETTY_FUNCTION ": forking");
1127 #endif
1129 /* Fork, exec cmd with args and optional env,
1130 * and return the handles with pid and blank
1131 * thread id
1133 pid=fork ();
1134 if(pid==-1) {
1135 process_handle_data->exec_errno=errno;
1136 } else if (pid==0) {
1137 /* child */
1138 int i;
1140 /* should we detach from the process
1141 * group? We're already running
1142 * without a controlling tty...
1145 /* Connect stdin, stdout and stderr */
1146 dup2 (fds[0], 0);
1147 dup2 (fds[1], 1);
1148 dup2 (fds[2], 2);
1150 if(process_fork.inherit!=TRUE) {
1151 /* FIXME: do something here */
1154 /* Close all file descriptors */
1155 for (i = getdtablesize () - 1; i > 2; i--) {
1156 close (i);
1159 /* pass process and thread handle info
1160 * to the child, so it doesn't have to
1161 * do an expensive search over the
1162 * whole list
1165 guint env_count=0;
1167 while(env[env_count]!=NULL) {
1168 env_count++;
1171 env=(char **)g_renew (char **, env, env_count+3);
1173 env[env_count]=g_strdup_printf ("_WAPI_PROCESS_HANDLE=%d", process_handle);
1174 env[env_count+1]=g_strdup_printf ("_WAPI_THREAD_HANDLE=%d", thread_handle);
1175 env[env_count+2]=NULL;
1178 #ifdef DEBUG
1179 g_message (G_GNUC_PRETTY_FUNCTION
1180 ": exec()ing [%s] in dir [%s]",
1181 cmd, dir);
1183 i=0;
1184 while(argv[i]!=NULL) {
1185 g_message ("arg %d: [%s]",
1186 i, argv[i]);
1187 i++;
1190 i=0;
1191 while(env[i]!=NULL) {
1192 g_message ("env %d: [%s]",
1193 i, env[i]);
1194 i++;
1197 #endif
1199 /* set cwd */
1200 if(chdir (dir)==-1) {
1201 process_handle_data->exec_errno=errno;
1202 exit (-1);
1205 /* exec */
1206 execve (argv[0], argv, env);
1208 /* bummer! */
1209 process_handle_data->exec_errno=errno;
1210 exit (-1);
1213 /* parent */
1215 /* store process name, based on the last section of the cmd */
1217 char *slash;
1219 /* This should never fail, but it seems it can...
1221 if (argv[0] != NULL) {
1222 slash=strrchr (argv[0], '/');
1224 if(slash!=NULL) {
1225 process_handle_data->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
1226 } else {
1227 process_handle_data->proc_name=_wapi_handle_scratch_store (argv[0], strlen (argv[0]));
1229 } else {
1230 process_handle_data->proc_name = _wapi_handle_scratch_store (cmd, strlen(cmd));
1234 /* These seem to be the defaults on w2k */
1235 process_handle_data->min_working_set=204800;
1236 process_handle_data->max_working_set=1413120;
1238 if(cmd!=NULL) {
1239 g_free (cmd);
1241 if(dir!=NULL) {
1242 g_free (dir);
1244 g_strfreev (argv);
1245 g_strfreev (env);
1247 /* store pid */
1248 process_handle_data->id=pid;
1249 process_handle_data->main_thread=GUINT_TO_POINTER (thread_handle);
1250 /* Ignore errors */
1251 gettimeofday (&tv, NULL);
1252 _wapi_timeval_to_filetime (&tv,
1253 &process_handle_data->create_time);
1255 /* FIXME: if env==0, inherit the env from the current
1256 * process
1258 process_handle_data->env=process_fork.env;
1260 thread_handle_data->process_handle=GUINT_TO_POINTER (process_handle);
1262 resp.u.process_fork.pid=pid;
1265 resp.u.process_fork.process_handle=process_handle;
1266 resp.u.process_fork.thread_handle=thread_handle;
1268 send_reply (channel, &resp);
1272 * process_set_share:
1273 * @channel: The client making the request
1274 * @channel_data: The channel data
1275 * @set_share: Set share data passed from the client
1277 * Sets file share info
1279 static void process_set_share (GIOChannel *channel, ChannelData *channel_data,
1280 WapiHandleRequest_SetShare set_share)
1282 WapiHandleResponse resp = {0};
1284 resp.type = WapiHandleResponseType_SetShare;
1286 #ifdef DEBUG
1287 g_message (G_GNUC_PRETTY_FUNCTION ": Setting share for file (dev:0x%llx, ino:%lld) mode 0x%x access 0x%x", set_share.device, set_share.inode, set_share.sharemode, set_share.access);
1288 #endif
1290 sharemode_set (set_share.device, set_share.inode, set_share.sharemode,
1291 set_share.access);
1293 send_reply (channel, &resp);
1297 * process_get_or_set_share:
1298 * @channel: The client making the request
1299 * @channel_data: The channel data
1300 * @get_share: GetOrSetShare data passed from the client
1302 * Gets a file share status, and sets the status if it doesn't already
1303 * exist
1305 static void process_get_or_set_share (GIOChannel *channel,
1306 ChannelData *channel_data,
1307 WapiHandleRequest_GetOrSetShare get_share)
1309 WapiHandleResponse resp = {0};
1311 resp.type = WapiHandleResponseType_GetOrSetShare;
1313 #ifdef DEBUG
1314 g_message (G_GNUC_PRETTY_FUNCTION
1315 ": Getting share status for file (dev:0x%llx, ino:%lld)",
1316 get_share.device, get_share.inode);
1317 #endif
1319 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);
1321 if (resp.u.get_or_set_share.exists) {
1322 #ifdef DEBUG
1323 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode: 0x%x",
1324 resp.u.get_or_set_share.sharemode);
1325 #endif
1326 } else {
1327 #ifdef DEBUG
1328 g_message (G_GNUC_PRETTY_FUNCTION
1329 ": file share info not already known, setting");
1330 #endif
1331 sharemode_set (get_share.device, get_share.inode,
1332 get_share.new_sharemode, get_share.new_access);
1335 send_reply (channel, &resp);
1339 * read_message:
1340 * @channel: The client to read the request from
1341 * @open_handles: An array of handles referenced by this client
1343 * Read a message (A WapiHandleRequest) from a client and dispatch
1344 * whatever it wants to the process_* calls. Return TRUE if the message
1345 * was read successfully, FALSE otherwise.
1347 static gboolean read_message (GIOChannel *channel, ChannelData *channel_data)
1349 WapiHandleRequest req;
1350 int fds[3]={0, 1, 2};
1351 int ret;
1352 gboolean has_fds=FALSE;
1354 /* Reading data */
1355 ret=_wapi_daemon_request (g_io_channel_unix_get_fd (channel), &req,
1356 fds, &has_fds);
1357 if(ret==0) {
1358 /* Other end went away */
1359 #ifdef DEBUG
1360 g_message ("Read 0 bytes on fd %d, closing it",
1361 g_io_channel_unix_get_fd (channel));
1362 #endif
1363 rem_fd (channel, channel_data);
1364 return(FALSE);
1367 #ifdef DEBUG
1368 g_message ("Process request %d", req.type);
1369 #endif
1370 switch(req.type) {
1371 case WapiHandleRequestType_New:
1372 process_new (channel, channel_data, req.u.new.type);
1373 break;
1374 case WapiHandleRequestType_Open:
1375 #ifdef DEBUG
1376 g_assert(req.u.open.handle < _wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
1377 #endif
1378 process_open (channel, channel_data, req.u.open.handle);
1379 break;
1380 case WapiHandleRequestType_Close:
1381 #ifdef DEBUG
1382 g_assert(req.u.close.handle < _wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
1383 #endif
1384 process_close (channel, channel_data, req.u.close.handle);
1385 break;
1386 case WapiHandleRequestType_Scratch:
1387 process_scratch (channel, req.u.scratch.length);
1388 break;
1389 case WapiHandleRequestType_ScratchFree:
1390 process_scratch_free (channel, req.u.scratch_free.idx);
1391 break;
1392 case WapiHandleRequestType_ProcessFork:
1393 process_process_fork (channel, channel_data,
1394 req.u.process_fork, fds);
1395 break;
1396 case WapiHandleRequestType_ProcessKill:
1397 process_process_kill (channel, req.u.process_kill);
1398 break;
1399 case WapiHandleRequestType_SetShare:
1400 process_set_share (channel, channel_data, req.u.set_share);
1401 break;
1402 case WapiHandleRequestType_GetOrSetShare:
1403 process_get_or_set_share (channel, channel_data,
1404 req.u.get_or_set_share);
1405 break;
1406 case WapiHandleRequestType_Error:
1407 /* fall through */
1408 default:
1409 /* Catch bogus requests */
1410 /* FIXME: call rem_fd? */
1411 break;
1414 if(has_fds==TRUE) {
1415 #ifdef DEBUG
1416 g_message (G_GNUC_PRETTY_FUNCTION ": closing %d", fds[0]);
1417 g_message (G_GNUC_PRETTY_FUNCTION ": closing %d", fds[1]);
1418 g_message (G_GNUC_PRETTY_FUNCTION ": closing %d", fds[2]);
1419 #endif
1421 close (fds[0]);
1422 close (fds[1]);
1423 close (fds[2]);
1426 return(TRUE);
1430 * fd_activity:
1431 * @channel: The IO channel that is active
1432 * @condition: The condition that has been satisfied
1433 * @data: A pointer to an array of handles referenced by this client
1435 * The callback called by the main loop when there is activity on an
1436 * IO channel.
1438 static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
1439 gpointer data)
1441 int fd=g_io_channel_unix_get_fd (channel);
1442 ChannelData *channel_data=&channels[fd];
1443 GMainContext *context=data;
1445 if(condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
1446 #ifdef DEBUG
1447 g_message ("fd %d error", fd);
1448 #endif
1450 rem_fd (channel, channel_data);
1451 return(FALSE);
1454 if(condition & (G_IO_IN | G_IO_PRI)) {
1455 if(fd==main_sock) {
1456 int newsock;
1457 struct sockaddr addr;
1458 socklen_t addrlen=sizeof(struct sockaddr);
1460 newsock=accept (main_sock, &addr, &addrlen);
1461 if(newsock==-1) {
1462 g_critical ("accept error: %s",
1463 g_strerror (errno));
1464 cleanup ();
1465 exit (-1);
1468 #ifdef DEBUG
1469 g_message ("accept returning %d", newsock);
1470 #endif
1472 add_fd (newsock, context);
1473 } else {
1474 #ifdef DEBUG
1475 g_message ("reading data on fd %d", fd);
1476 #endif
1478 return(read_message (channel, channel_data));
1480 return(TRUE);
1483 return(FALSE); /* remove source */
1487 * _wapi_daemon_main:
1489 * Open socket, create shared mem segment and begin listening for
1490 * clients.
1492 void _wapi_daemon_main(gpointer data, gpointer scratch)
1494 struct sockaddr_un main_socket_address;
1495 int ret;
1496 GMainContext *context;
1498 #ifdef DEBUG
1499 g_message ("Starting up...");
1500 #endif
1502 _wapi_shared_data[0]=data;
1503 _wapi_shared_scratch=scratch;
1504 _wapi_shared_scratch->is_shared=TRUE;
1506 /* Note that we've got the starting segment already */
1507 _wapi_shared_data[0]->num_segments=1;
1508 _wapi_shm_mapped_segments=1;
1510 _wapi_fd_offset_table_size=getdtablesize ();
1511 _wapi_shared_data[0]->fd_offset_table_size = _wapi_fd_offset_table_size;
1513 startup ();
1515 main_sock=socket(PF_UNIX, SOCK_STREAM, 0);
1517 main_socket_address.sun_family=AF_UNIX;
1518 memcpy(main_socket_address.sun_path, _wapi_shared_data[0]->daemon,
1519 MONO_SIZEOF_SUNPATH);
1521 ret=bind(main_sock, (struct sockaddr *)&main_socket_address,
1522 sizeof(struct sockaddr_un));
1523 if(ret==-1) {
1524 g_critical ("bind failed: %s", g_strerror (errno));
1525 _wapi_shared_data[0]->daemon_running=DAEMON_DIED_AT_STARTUP;
1526 exit(-1);
1529 #ifdef DEBUG
1530 g_message("bound");
1531 #endif
1533 ret=listen(main_sock, 5);
1534 if(ret==-1) {
1535 g_critical ("listen failed: %s", g_strerror (errno));
1536 _wapi_shared_data[0]->daemon_running=DAEMON_DIED_AT_STARTUP;
1537 exit(-1);
1540 #ifdef DEBUG
1541 g_message("listening");
1542 #endif
1544 context = g_main_context_new ();
1546 add_fd(main_sock, context);
1548 /* We're finished setting up, let everyone else know we're
1549 * ready. From now on, it's up to us to delete the shared
1550 * memory segment when appropriate.
1552 _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING;
1554 while(TRUE) {
1555 if(check_processes==TRUE) {
1556 process_died ();
1559 #ifdef DEBUG
1560 g_message ("polling");
1561 #endif
1563 /* Block until something happens. We don't use
1564 * g_main_loop_run() because we rely on the SIGCHLD
1565 * signal interrupting poll() so we can reap child
1566 * processes as soon as they die, without burning cpu
1567 * time by polling the flag.
1569 g_main_context_iteration (context, TRUE);