2 * daemon.c: The handle daemon
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
14 #include <sys/types.h>
15 #include <sys/socket.h>
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>
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 */
45 /* Arrays to keep track of channel data for the
46 * daemon and clients indexed by file descriptor
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 */
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 */
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
72 typedef struct _share_data
78 static GHashTable
*file_share_hash
= NULL
;
80 static gboolean
fd_activity (GIOChannel
*channel
, GIOCondition condition
,
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)
91 #ifdef NEED_LINK_UNLINK
92 unlink(_wapi_shared_data
[0]->daemon
);
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
107 static void maybe_exit (void)
112 g_message (G_GNUC_PRETTY_FUNCTION
": Seeing if we should exit");
117 g_message (G_GNUC_PRETTY_FUNCTION
": Still got clients");
122 /* Prevent new clients from connecting... */
123 _wapi_shared_data
[0]->daemon_running
=DAEMON_CLOSING
;
126 i
<_wapi_shared_data
[0]->num_segments
* _WAPI_HANDLES_PER_SEGMENT
;
128 if(daemon_channel_data
->open_handles
[i
]>0) {
130 g_message (G_GNUC_PRETTY_FUNCTION
131 ": Still got handle references");
134 _wapi_shared_data
[0]->daemon_running
=DAEMON_RUNNING
;
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];
156 fds
[0].events
=POLLIN
;
160 g_message (G_GNUC_PRETTY_FUNCTION
": Last connect check");
163 if(poll (fds
, 1, 0)>0) {
164 /* Someone did connect, so carry on running */
166 g_message (G_GNUC_PRETTY_FUNCTION
167 ": Someone connected");
170 _wapi_shared_data
[0]->daemon_running
=DAEMON_RUNNING
;
177 g_message (G_GNUC_PRETTY_FUNCTION
": Byebye");
188 * Called if daemon receives a SIGTERM or SIGINT
190 static void signal_handler (int signo
)
193 g_message (G_GNUC_PRETTY_FUNCTION
": daemon received signal %d", signo
);
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
);
231 * Bind signals, attach to shared memory and set up any internal data
234 static void startup (void)
238 sa
.sa_handler
=signal_handler
;
239 sigemptyset (&sa
.sa_mask
);
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
);
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 (),
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
266 snprintf (_wapi_shared_data
[0]->daemon
+1, MONO_SIZEOF_SUNPATH
-2,
267 "mono-handle-daemon-%d-%d-%ld", getuid (), getpid (),
271 file_share_hash
= g_hash_table_new_full (sharedata_hash
,
272 sharedata_equal
, g_free
,
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
285 static void ref_handle (ChannelData
*channel_data
, guint32 handle
)
287 guint32 segment
, idx
;
293 _wapi_handle_segment (GUINT_TO_POINTER (handle
), &segment
, &idx
);
295 _wapi_shared_data
[segment
]->handles
[idx
].ref
++;
296 channel_data
->open_handles
[handle
]++;
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
]);
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
;
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",
331 _wapi_handle_segment (GUINT_TO_POINTER (handle
), &segment
, &idx
);
333 _wapi_shared_data
[segment
]->handles
[idx
].ref
--;
334 channel_data
->open_handles
[handle
]--;
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
]);
343 if (_wapi_shared_data
[segment
]->handles
[idx
].ref
== 0) {
348 /* This client has released the handle */
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
]);
357 g_message (G_GNUC_PRETTY_FUNCTION
": Destroying handle 0x%x",
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
);
368 struct _WapiHandle_file
*file_handle
;
371 ok
= _wapi_lookup_handle (GUINT_TO_POINTER (handle
),
373 (gpointer
*)&file_handle
,
376 g_warning (G_GNUC_PRETTY_FUNCTION
377 ": error looking up file handle %x",
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
);
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
;
396 check_sharing (device
, inode
);
400 if(channel_data
== daemon_channel_data
) {
401 /* The daemon released a reference, so see if it's
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
;
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_
434 int old_len
=channels_length
* sizeof(ChannelData
);
436 channels_length
=fd
+10;
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
];
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
,
463 channels
[fd
].io_source
=g_source_attach (source
, context
);
464 g_source_unref (source
);
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
;
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!");
491 g_message (G_GNUC_PRETTY_FUNCTION
": Removing client fd %d", fd
);
494 if (channel_data
->io_source
== 0) {
496 g_message (G_GNUC_PRETTY_FUNCTION
": channel already closed for fd %d", fd
);
502 g_io_channel_shutdown (channel
, TRUE
, NULL
);
503 g_source_remove (channel_data
->io_source
);
504 g_io_channel_unref (channel
);
507 i
<_wapi_shared_data
[0]->num_segments
* _WAPI_HANDLES_PER_SEGMENT
;
509 handle_count
=channel_data
->open_handles
[i
];
511 for(j
=0; j
<handle_count
; j
++) {
513 g_message (G_GNUC_PRETTY_FUNCTION
": closing handle 0x%x for client at index %d", i
, g_io_channel_unix_get_fd (channel
));
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;
528 /* Just the master socket left, so see if we can
535 static void sharemode_set (dev_t device
, ino_t inode
, guint32 sharemode
,
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
);
555 g_hash_table_insert (file_share_hash
, sharekey
, sharedata
);
559 static gboolean
sharemode_get (dev_t device
, ino_t inode
, guint32
*sharemode
,
563 ShareData
*sharedata
;
565 sharekey
.device
= device
;
566 sharekey
.inode
= inode
;
568 sharedata
= (ShareData
*)g_hash_table_lookup (file_share_hash
,
570 if (sharedata
== NULL
) {
574 *sharemode
= sharedata
->sharemode
;
575 *access
= sharedata
->access
;
580 static gboolean
share_compare (gpointer handle
, gpointer user_data
)
582 struct _WapiHandle_file
*file_handle
;
584 ShareKey
*sharekey
= (ShareKey
*)user_data
;
586 ok
= _wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
587 (gpointer
*)&file_handle
, NULL
);
589 g_warning (G_GNUC_PRETTY_FUNCTION
590 ": error looking up file handle %p", handle
);
594 if (file_handle
->device
== sharekey
->device
&&
595 file_handle
->inode
== sharekey
->inode
) {
597 g_message (G_GNUC_PRETTY_FUNCTION
": found one, handle %p",
606 static void check_sharing (dev_t device
, ino_t inode
)
609 gpointer file_handle
;
612 g_message (G_GNUC_PRETTY_FUNCTION
": Checking if anything has (dev 0x%llx, inode %lld) still open", device
, inode
);
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
626 g_message (G_GNUC_PRETTY_FUNCTION
": Deleting share data for (dev 0x%llx inode %lld)", device
, inode
);
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
;
638 guint32 segment
, idx
;
640 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_PROCESS
,
641 (gpointer
*)&process_handle
, NULL
);
643 g_warning (G_GNUC_PRETTY_FUNCTION
644 ": error looking up process handle %p", handle
);
648 _wapi_handle_segment (handle
, &segment
, &idx
);
649 if (_wapi_shared_data
[segment
]->handles
[idx
].signalled
) {
653 pid
=GPOINTER_TO_UINT (user_data
);
654 if(process_handle
->id
==pid
) {
661 static gboolean
process_thread_compare (gpointer handle
, gpointer user_data
)
663 struct _WapiHandle_thread
*thread_handle
;
665 guint32 segment
, idx
;
667 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_THREAD
,
668 (gpointer
*)&thread_handle
, NULL
);
670 g_warning (G_GNUC_PRETTY_FUNCTION
671 ": error looking up thread handle %p", handle
);
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.
681 g_message (G_GNUC_PRETTY_FUNCTION
": Set thread handle %p signalled, because its process died", handle
);
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
);
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 */
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
,
721 GUINT_TO_POINTER (pid
),
722 (gpointer
*)&process_handle_data
,
724 if(process_handle
==0) {
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
);
734 /* Signal the handle. Don't use
735 * _wapi_handle_set_signal_state() unless we have
736 * process-shared pthread support.
741 g_message (G_GNUC_PRETTY_FUNCTION
742 ": Set process %d exitstatus to %d", pid
,
743 WEXITSTATUS (status
));
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
);
753 process_handle_data
->exitstatus
=WEXITSTATUS (status
);
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
);
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)
794 check_processes
=FALSE
;
797 g_message (G_GNUC_PRETTY_FUNCTION
": Reaping processes");
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)
810 /* pid contains the ID of a dead process */
812 g_message (G_GNUC_PRETTY_FUNCTION
": process %d reaped", pid
);
814 process_post_mortem (pid
, status
);
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
)
830 _wapi_daemon_response (g_io_channel_unix_get_fd (channel
), resp
);
833 static guint32
new_handle_with_shared_check (WapiHandleType type
)
837 while ((handle
= _wapi_handle_new_internal (type
)) == 0) {
838 /* Try and allocate a new shared segment, and have
841 guint32 segment
=_wapi_shared_data
[0]->num_segments
;
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
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
);
863 /* Map failed. Just return 0 meaning "out of
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
,
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
894 ref_handle (channel_data
, handle
);
897 g_message (G_GNUC_PRETTY_FUNCTION
": returning new handle 0x%x",
901 resp
.type
=WapiHandleResponseType_New
;
902 resp
.u
.new.type
=type
;
903 resp
.u
.new.handle
=handle
;
905 send_reply (channel
, &resp
);
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
,
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
);
931 g_message (G_GNUC_PRETTY_FUNCTION
932 ": returning new handle 0x%x", handle
);
935 resp
.type
=WapiHandleResponseType_Open
;
936 resp
.u
.new.type
=shared
->type
;
937 resp
.u
.new.handle
=handle
;
939 send_reply (channel
, &resp
);
944 resp
.type
=WapiHandleResponseType_Open
;
947 send_reply (channel
, &resp
);
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
,
962 WapiHandleResponse resp
={0};
964 resp
.type
=WapiHandleResponseType_Close
;
965 resp
.u
.close
.destroy
=unref_handle (channel_data
, handle
);
968 g_message (G_GNUC_PRETTY_FUNCTION
": unreffing handle 0x%x", handle
);
971 send_reply (channel
, &resp
);
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
);
988 g_message (G_GNUC_PRETTY_FUNCTION
": allocating scratch index 0x%x",
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
);
1010 g_message (G_GNUC_PRETTY_FUNCTION
": deleting scratch index 0x%x",
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.
1025 process_process_kill (GIOChannel
*channel
,
1026 WapiHandleRequest_ProcessKill process_kill
)
1028 WapiHandleResponse resp
= {0};
1030 resp
.type
= WapiHandleResponseType_ProcessKill
;
1033 g_message (G_GNUC_PRETTY_FUNCTION
": kill (%d, %d)",
1034 process_kill
.pid
, process_kill
.signo
);
1036 if (kill (process_kill
.pid
, process_kill
.signo
) == -1) {
1037 resp
.u
.process_kill
.err
= errno
;
1039 g_message (G_GNUC_PRETTY_FUNCTION
": kill (%d, %d) failed: %d",
1040 process_kill
.pid
, process_kill
.signo
, resp
.u
.process_kill
.err
);
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
1056 static void process_process_fork (GIOChannel
*channel
, ChannelData
*channel_data
,
1057 WapiHandleRequest_ProcessFork process_fork
,
1060 WapiHandleResponse resp
={0};
1061 guint32 process_handle
, thread_handle
;
1062 struct _WapiHandle_process
*process_handle_data
;
1063 struct _WapiHandle_thread
*thread_handle_data
;
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
);
1091 char *cmd
=NULL
, *dir
=NULL
, **argv
, **env
;
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
,
1113 _wapi_lookup_handle (GUINT_TO_POINTER (thread_handle
),
1115 (gpointer
*)&thread_handle_data
,
1118 ret
=g_shell_parse_argv (cmd
, NULL
, &argv
, &gerr
);
1120 /* FIXME: Could do something with the
1123 process_handle_data
->exec_errno
=gerr
->code
;
1126 g_message (G_GNUC_PRETTY_FUNCTION
": forking");
1129 /* Fork, exec cmd with args and optional env,
1130 * and return the handles with pid and blank
1135 process_handle_data
->exec_errno
=errno
;
1136 } else if (pid
==0) {
1140 /* should we detach from the process
1141 * group? We're already running
1142 * without a controlling tty...
1145 /* Connect stdin, stdout and stderr */
1150 if(process_fork
.inherit
!=TRUE
) {
1151 /* FIXME: do something here */
1154 /* Close all file descriptors */
1155 for (i
= getdtablesize () - 1; i
> 2; 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
1167 while(env
[env_count
]!=NULL
) {
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
;
1179 g_message (G_GNUC_PRETTY_FUNCTION
1180 ": exec()ing [%s] in dir [%s]",
1184 while(argv
[i
]!=NULL
) {
1185 g_message ("arg %d: [%s]",
1191 while(env
[i
]!=NULL
) {
1192 g_message ("env %d: [%s]",
1200 if(chdir (dir
)==-1) {
1201 process_handle_data
->exec_errno
=errno
;
1206 execve (argv
[0], argv
, env
);
1209 process_handle_data
->exec_errno
=errno
;
1215 /* store process name, based on the last section of the cmd */
1219 /* This should never fail, but it seems it can...
1221 if (argv
[0] != NULL
) {
1222 slash
=strrchr (argv
[0], '/');
1225 process_handle_data
->proc_name
=_wapi_handle_scratch_store (slash
+1, strlen (slash
+1));
1227 process_handle_data
->proc_name
=_wapi_handle_scratch_store (argv
[0], strlen (argv
[0]));
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;
1248 process_handle_data
->id
=pid
;
1249 process_handle_data
->main_thread
=GUINT_TO_POINTER (thread_handle
);
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
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
;
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
);
1290 sharemode_set (set_share
.device
, set_share
.inode
, set_share
.sharemode
,
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
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
;
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
);
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
) {
1323 g_message (G_GNUC_PRETTY_FUNCTION
": Share mode: 0x%x",
1324 resp
.u
.get_or_set_share
.sharemode
);
1328 g_message (G_GNUC_PRETTY_FUNCTION
1329 ": file share info not already known, setting");
1331 sharemode_set (get_share
.device
, get_share
.inode
,
1332 get_share
.new_sharemode
, get_share
.new_access
);
1335 send_reply (channel
, &resp
);
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};
1352 gboolean has_fds
=FALSE
;
1355 ret
=_wapi_daemon_request (g_io_channel_unix_get_fd (channel
), &req
,
1358 /* Other end went away */
1360 g_message ("Read 0 bytes on fd %d, closing it",
1361 g_io_channel_unix_get_fd (channel
));
1363 rem_fd (channel
, channel_data
);
1368 g_message ("Process request %d", req
.type
);
1371 case WapiHandleRequestType_New
:
1372 process_new (channel
, channel_data
, req
.u
.new.type
);
1374 case WapiHandleRequestType_Open
:
1376 g_assert(req
.u
.open
.handle
< _wapi_shared_data
[0]->num_segments
* _WAPI_HANDLES_PER_SEGMENT
);
1378 process_open (channel
, channel_data
, req
.u
.open
.handle
);
1380 case WapiHandleRequestType_Close
:
1382 g_assert(req
.u
.close
.handle
< _wapi_shared_data
[0]->num_segments
* _WAPI_HANDLES_PER_SEGMENT
);
1384 process_close (channel
, channel_data
, req
.u
.close
.handle
);
1386 case WapiHandleRequestType_Scratch
:
1387 process_scratch (channel
, req
.u
.scratch
.length
);
1389 case WapiHandleRequestType_ScratchFree
:
1390 process_scratch_free (channel
, req
.u
.scratch_free
.idx
);
1392 case WapiHandleRequestType_ProcessFork
:
1393 process_process_fork (channel
, channel_data
,
1394 req
.u
.process_fork
, fds
);
1396 case WapiHandleRequestType_ProcessKill
:
1397 process_process_kill (channel
, req
.u
.process_kill
);
1399 case WapiHandleRequestType_SetShare
:
1400 process_set_share (channel
, channel_data
, req
.u
.set_share
);
1402 case WapiHandleRequestType_GetOrSetShare
:
1403 process_get_or_set_share (channel
, channel_data
,
1404 req
.u
.get_or_set_share
);
1406 case WapiHandleRequestType_Error
:
1409 /* Catch bogus requests */
1410 /* FIXME: call rem_fd? */
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]);
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
1438 static gboolean
fd_activity (GIOChannel
*channel
, GIOCondition condition
,
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
)) {
1447 g_message ("fd %d error", fd
);
1450 rem_fd (channel
, channel_data
);
1454 if(condition
& (G_IO_IN
| G_IO_PRI
)) {
1457 struct sockaddr addr
;
1458 socklen_t addrlen
=sizeof(struct sockaddr
);
1460 newsock
=accept (main_sock
, &addr
, &addrlen
);
1462 g_critical ("accept error: %s",
1463 g_strerror (errno
));
1469 g_message ("accept returning %d", newsock
);
1472 add_fd (newsock
, context
);
1475 g_message ("reading data on fd %d", fd
);
1478 return(read_message (channel
, channel_data
));
1483 return(FALSE
); /* remove source */
1487 * _wapi_daemon_main:
1489 * Open socket, create shared mem segment and begin listening for
1492 void _wapi_daemon_main(gpointer data
, gpointer scratch
)
1494 struct sockaddr_un main_socket_address
;
1496 GMainContext
*context
;
1499 g_message ("Starting up...");
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
;
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
));
1524 g_critical ("bind failed: %s", g_strerror (errno
));
1525 _wapi_shared_data
[0]->daemon_running
=DAEMON_DIED_AT_STARTUP
;
1533 ret
=listen(main_sock
, 5);
1535 g_critical ("listen failed: %s", g_strerror (errno
));
1536 _wapi_shared_data
[0]->daemon_running
=DAEMON_DIED_AT_STARTUP
;
1541 g_message("listening");
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
;
1555 if(check_processes
==TRUE
) {
1560 g_message ("polling");
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
);