Lots of magic
[kopensolaris-gnu/glibc.git] / sysdeps / unix / sysv / solaris2 / kopensolaris-gnu / door.c
1 /* Copyright (C) 2008 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by David Bartley <dtbartle@csclub.uwaterloo.ca>, 2008.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <sysdep-cancel.h>
21 #include <inline-syscall.h>
22 #include <doorP.h>
23 #include <ucredP.h>
24 #include <libio/libioP.h>
25 #include <atomic.h>
26 #include <thread.h>
27 #include <dlfcn.h>
28 #include <alloca.h>
29
30 static pid_t __door_private_pid, __door_unref_pid;
31 static void * door_server_create_default (door_info_t *);
32 static door_server_func_t *door_server_create_proc = &door_server_create_default;
33 static int (*thr_create_ptr) (void *, size_t, void * (*)(void *), void *,
34     long, thread_t *);
35
36 /* Arguments are passed normally with the 6th argument always the subcode.  */
37
38 DECLARE_INLINE_SYSCALL (int, door, long, long, long, long, long, long subcode);
39
40
41 int door_info (int d, door_info_t *info)
42 {
43   return INLINE_SYSCALL (door, 6, d, (long)info, 0, 0, 0, SYS_SUB_door_info);
44 }
45
46
47 int door_bind (int d)
48 {
49   return INLINE_SYSCALL (door, 6, d, 0, 0, 0, 0, SYS_SUB_door_bind);
50 }
51
52
53 int door_unbind (void)
54 {
55   return INLINE_SYSCALL (door, 6, 0, 0, 0, 0, 0, SYS_SUB_door_unbind);
56 }
57
58
59 int door_revoke (int d)
60 {
61   return INLINE_SYSCALL (door, 6, d, 0, 0, 0, 0, SYS_SUB_door_revoke);
62 }
63
64
65 int door_getparam(int d, int param, size_t *out)
66 {
67   return INLINE_SYSCALL (door, 6, d, param, (long)out, 0, 0,
68     SYS_SUB_door_getparam);
69 }
70
71
72 int door_setparam (int d, int param, size_t val)
73 {
74   return INLINE_SYSCALL (door, 6, d, param, val, 0, 0, SYS_SUB_door_setparam);
75 }
76
77
78 int door_call (int d, door_arg_t* params)
79 {
80   if (SINGLE_THREAD_P)
81     return INLINE_SYSCALL (door, 6, d, (long)params, 0, 0, 0,
82       SYS_SUB_door_call);
83
84   int oldtype = LIBC_CANCEL_ASYNC ();
85
86   int res = INLINE_SYSCALL (door, 6, d, (long)params, 0, 0, 0,
87     SYS_SUB_door_call);
88
89   LIBC_CANCEL_RESET (oldtype);
90
91   return res;
92 }
93
94
95 int door_return (char *data_ptr, size_t data_size, door_desc_t *desc_ptr,
96      uint_t num_desc)
97 {
98   ucontext_t uc;
99   if (getcontext (&uc) != 0)
100     return -1;
101
102   door_return_desc_t drd;
103   drd.desc_ptr = desc_ptr;
104   drd.desc_num = num_desc;
105
106   return INLINE_SYSCALL (door, 6, (long)data_ptr, data_size, (long)&drd,
107       (long)uc.uc_stack.ss_sp, uc.uc_stack.ss_size, SYS_SUB_door_return);
108 }
109
110
111 door_server_func_t * door_server_create (door_server_func_t *create_proc)
112 {
113   while (1)
114     {
115       door_server_func_t *cur_proc = door_server_create_proc;
116       door_server_func_t *old_proc = atomic_compare_and_exchange_val_acq (
117           &door_server_create_proc, create_proc, cur_proc);
118       if (old_proc == cur_proc)
119         return old_proc;
120     }
121 }
122
123
124 static void * door_create_default_proc (void *arg)
125 {
126   pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
127   door_return (NULL, 0, NULL, 0);
128
129   return arg;
130 }
131
132
133 static void * door_server_create_default (door_info_t *info)
134 {
135   if (!thr_create_ptr)
136     {
137       void *libpthread = __libc_dlopen ("libpthread.so");
138       if (!libpthread)
139         return NULL;
140       thr_create_ptr = __libc_dlsym (libpthread, "thr_create");
141       if (!thr_create_ptr)
142         return NULL;
143     }
144
145   /* The default server create action is to create a server thread. We use
146      thr_create since we want to create this as a daemon thread.  */
147   thr_create_ptr (NULL, 0, door_create_default_proc, NULL, THR_DETACHED, NULL);
148
149   return NULL;
150 }
151
152
153 static void * door_unref_proc (void *arg)
154 {
155   /* We may get interrupted so loop.  */
156   while (INLINE_SYSCALL (door, 6, 0, 0, 0, 0, 0, SYS_SUB_door_unrefsys) &&
157       errno == EINTR) ;
158
159   return NULL;
160 }
161
162
163 int door_create (void (*server_procedure)(void *cookie, char *argp,
164       size_t arg_size, door_desc_t *dp, uint_t n_desc), void *cookie,
165       unsigned int attributes)
166 {
167   // TODO: remove
168   if (attributes & ~(DOOR_NO_CANCEL | DOOR_REFUSE_DESC | DOOR_PRIVATE | \
169         DOOR_UNREF | DOOR_UNREF_MULTI))
170     abort ();
171
172   /* We lock the io list lock as fork() locks it before forking. This allows us
173      to be safe in the face of a fork.  */
174   _IO_list_lock ();
175
176   return INLINE_SYSCALL (door, 6, (long)server_procedure, (long)cookie, attributes,
177       0, 0, SYS_SUB_door_create);
178
179   pid_t pid = getpid ();
180   if (__door_private_pid != pid && (attributes & DOOR_PRIVATE) == 0)
181     {
182       /* We haven't created the first server.  */
183       (*door_server_create_proc) (NULL);
184       __door_private_pid = pid;
185     }
186   if (__door_unref_pid != pid && (attributes & (DOOR_UNREF | DOOR_UNREF_MULTI)))
187     {
188       /* We haven't created the unreferenced thread.  */
189       thr_create (NULL, 0, door_unref_proc, NULL, THR_DAEMON, NULL);
190       __door_unref_pid = pid;
191     }
192
193   _IO_list_unlock ();
194 }
195
196
197 int door_ucred (ucred_t **info)
198 {
199   ucred_t *uc = *info;
200   if (!uc)
201     {
202       uc = _ucred_alloc ();
203       if (!uc)
204         return -1;
205     }
206
207   int res = INLINE_SYSCALL (door, 6, (long)uc, 0, 0, 0, 0, SYS_SUB_door_ucred);
208   if (res != 0)
209     {
210       if (!*info)
211         free (uc);
212       return -1;
213     }
214
215   *info = uc;
216
217   return 0;
218 }
219
220
221 int door_cred (door_cred_t *info)
222 {
223   ucred_t *uc = alloca (ucred_size ());
224   int res = INLINE_SYSCALL (door, 6, (long)uc, 0, 0, 0, 0, SYS_SUB_door_ucred);
225   if (res != 0)
226     return -1;
227
228   info->dc_euid = ucred_geteuid (uc);
229   info->dc_ruid = ucred_getruid (uc);
230   info->dc_egid = ucred_getegid (uc);
231   info->dc_rgid = ucred_getrgid (uc);
232   info->dc_pid = ucred_getpid (uc);
233
234   return 0;
235 }