1 /* Copyright (C) 1994-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <stddef.h>
19 #include <errno.h>
20 #include <sys/time.h>
21 #include <time.h>
22 #include <hurd.h>
23 #include <hurd/signal.h>
24 #include <hurd/sigpreempt.h>
25 #include <hurd/msg_request.h>
26 #include <mach.h>
27 #include <mach/message.h>
28 
29 /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM.  */
30 
31 spin_lock_t _hurd_itimer_lock = SPIN_LOCK_INITIALIZER;
32 struct itimerval _hurd_itimerval; /* Current state of the timer.  */
33 mach_port_t _hurd_itimer_port;	/* Port the timer thread blocks on.  */
34 thread_t _hurd_itimer_thread;	/* Thread waiting for timeout.  */
35 int _hurd_itimer_thread_suspended; /* Nonzero if that thread is suspended.  */
36 vm_address_t _hurd_itimer_thread_stack_base; /* Base of its stack.  */
37 vm_size_t _hurd_itimer_thread_stack_size; /* Size of its stack.  */
38 struct timeval _hurd_itimer_started; /* Time the thread started waiting.  */
39 
40 static void
quantize_timeval(struct timeval * tv)41 quantize_timeval (struct timeval *tv)
42 {
43   static time_t quantum = -1;
44 
45   if (quantum == -1)
46     quantum = 1000000 / __getclktck ();
47 
48   tv->tv_usec = ((tv->tv_usec + (quantum - 1)) / quantum) * quantum;
49   if (tv->tv_usec >= 1000000)
50     {
51       ++tv->tv_sec;
52       tv->tv_usec -= 1000000;
53     }
54 }
55 
56 static inline void
subtract_timeval(struct timeval * from,const struct timeval * subtract)57 subtract_timeval (struct timeval *from, const struct timeval *subtract)
58 {
59   from->tv_usec -= subtract->tv_usec;
60   from->tv_sec -= subtract->tv_sec;
61   while (from->tv_usec < 0)
62     {
63       --from->tv_sec;
64       from->tv_usec += 1000000;
65     }
66 }
67 
68 /* Function run by the itimer thread.
69    This code must be very careful not ever to require a MiG reply port.  */
70 
71 static void
timer_thread(void)72 timer_thread (void)
73 {
74   while (1)
75     {
76       error_t err;
77       /* The only message we ever expect to receive is the reply from the
78          signal thread to a sig_post call we did.  We never examine the
79 	 contents.  */
80       struct
81 	{
82 	  mach_msg_header_t header;
83 	  mach_msg_type_t return_code_type;
84 	  error_t return_code;
85 	} msg;
86 
87       /* Wait for a message on a port that noone sends to.  The purpose is
88 	 the receive timeout.  Notice interrupts so that if we are
89 	 thread_abort'd, we will loop around and fetch new values from
90 	 _hurd_itimerval.  */
91       err = __mach_msg (&msg.header,
92 			MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_INTERRUPT,
93 			0, sizeof(msg), _hurd_itimer_port,
94 			_hurd_itimerval.it_value.tv_sec * 1000
95 			+ _hurd_itimerval.it_value.tv_usec / 1000,
96 			MACH_PORT_NULL);
97       switch (err)
98 	{
99 	case MACH_RCV_TIMED_OUT:
100 	  /* We got the expected timeout.  Send a message to the signal
101 	     thread to tell it to post a SIGALRM signal.  We use
102 	     _hurd_itimer_port as the reply port just so we will block until
103 	     the signal thread has frobnicated things to reload the itimer or
104 	     has terminated this thread.  */
105 	  __msg_sig_post_request (_hurd_msgport,
106 				  _hurd_itimer_port,
107 				  MACH_MSG_TYPE_MAKE_SEND_ONCE,
108 				  SIGALRM, SI_TIMER, __mach_task_self ());
109 	  break;
110 
111 	case MACH_RCV_INTERRUPTED:
112 	  /* We were thread_abort'd.  This is to tell us that
113 	     _hurd_itimerval has changed and we need to reexamine it
114 	     and start waiting with the new timeout value.  */
115 	  break;
116 
117 	case MACH_MSG_SUCCESS:
118 	  /* We got the reply message from the sig_post_request above.
119 	     Ignore it and reexamine the timer value.  */
120 	  __mach_msg_destroy (&msg.header); /* Just in case.  */
121 	  break;
122 
123 	default:
124 	  /* Unexpected lossage.  Oh well, keep trying.  */
125 	  break;
126 	}
127     }
128 }
129 
130 
131 /* Forward declaration.  */
132 static int setitimer_locked (const struct itimerval *new,
133 			     struct itimerval *old, void *crit,
134 			     int hurd_siglocked);
135 
136 static sighandler_t
restart_itimer(struct hurd_signal_preemptor * preemptor,struct hurd_sigstate * ss,int * signo,struct hurd_signal_detail * detail)137 restart_itimer (struct hurd_signal_preemptor *preemptor,
138 		struct hurd_sigstate *ss,
139 		int *signo, struct hurd_signal_detail *detail)
140 {
141   /* This function gets called in the signal thread
142      each time a SIGALRM is arriving (even if blocked).  */
143   struct itimerval it;
144 
145   /* Either reload or disable the itimer.  */
146   __spin_lock (&_hurd_itimer_lock);
147   it.it_value = it.it_interval = _hurd_itimerval.it_interval;
148   setitimer_locked (&it, NULL, NULL, 1);
149 
150   /* Continue with normal delivery (or hold, etc.) of SIGALRM.  */
151   return SIG_ERR;
152 }
153 
154 
155 /* Called before any normal SIGALRM signal is delivered.
156    Reload the itimer, or disable the itimer.  */
157 
158 static int
setitimer_locked(const struct itimerval * new,struct itimerval * old,void * crit,int hurd_siglocked)159 setitimer_locked (const struct itimerval *new, struct itimerval *old,
160 		  void *crit, int hurd_siglocked)
161 {
162   struct itimerval newval;
163   struct timeval now, remaining, elapsed;
164   struct timeval old_interval;
165   error_t err;
166 
167   inline void kill_itimer_thread (void)
168     {
169       __thread_terminate (_hurd_itimer_thread);
170       __vm_deallocate (__mach_task_self (),
171 		       _hurd_itimer_thread_stack_base,
172 		       _hurd_itimer_thread_stack_size);
173       _hurd_itimer_thread = MACH_PORT_NULL;
174     }
175 
176   if (!new)
177     {
178       /* Just return the current value in OLD without changing anything.
179 	 This is what BSD does, even though it's not documented. */
180       if (old)
181 	*old = _hurd_itimerval;
182       spin_unlock (&_hurd_itimer_lock);
183       _hurd_critical_section_unlock (crit);
184       return 0;
185     }
186 
187   newval = *new;
188   quantize_timeval (&newval.it_interval);
189   quantize_timeval (&newval.it_value);
190   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0)
191     {
192       /* Make sure the itimer thread is set up.  */
193 
194       /* Set up a signal preemptor global for all threads to
195 	 run `restart_itimer' each time a SIGALRM would arrive.  */
196       static struct hurd_signal_preemptor preemptor =
197 	{
198 	  __sigmask (SIGALRM), SI_TIMER, SI_TIMER,
199 	  &restart_itimer,
200 	};
201       if (!hurd_siglocked)
202 	__mutex_lock (&_hurd_siglock);
203       if (! preemptor.next && _hurdsig_preemptors != &preemptor)
204 	{
205 	  preemptor.next = _hurdsig_preemptors;
206 	  _hurdsig_preemptors = &preemptor;
207 	  _hurdsig_preempted_set |= preemptor.signals;
208 	}
209       if (!hurd_siglocked)
210 	__mutex_unlock (&_hurd_siglock);
211 
212       if (_hurd_itimer_port == MACH_PORT_NULL)
213 	{
214 	  /* Allocate a receive right that the itimer thread will
215 	     block waiting for a message on.  */
216 	  if (err = __mach_port_allocate (__mach_task_self (),
217 					  MACH_PORT_RIGHT_RECEIVE,
218 					  &_hurd_itimer_port))
219 	    goto out;
220 	}
221 
222       if (_hurd_itimer_thread == MACH_PORT_NULL)
223 	{
224 	  /* Start up the itimer thread running `timer_thread' (below).  */
225 	  if (err = __thread_create (__mach_task_self (),
226 				     &_hurd_itimer_thread))
227 	    goto out;
228 	  _hurd_itimer_thread_stack_base = 0; /* Anywhere.  */
229 	  _hurd_itimer_thread_stack_size = __vm_page_size; /* Small stack.  */
230 	  if ((err = __mach_setup_thread (__mach_task_self (),
231 					 _hurd_itimer_thread,
232 					 &timer_thread,
233 					 &_hurd_itimer_thread_stack_base,
234 					 &_hurd_itimer_thread_stack_size))
235 	      || (err = __mach_setup_tls(_hurd_itimer_thread)))
236 	    {
237 	      __thread_terminate (_hurd_itimer_thread);
238 	      _hurd_itimer_thread = MACH_PORT_NULL;
239 	      goto out;
240 	    }
241 	  _hurd_itimer_thread_suspended = 1;
242 	}
243     }
244 
245   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
246     {
247       /* Calculate how much time is remaining for the pending alarm.  */
248       {
249 	time_value_t tv;
250 	__host_get_time (__mach_host_self (), &tv);
251 	now.tv_sec = tv.seconds;
252 	now.tv_usec = tv.microseconds;
253       }
254       elapsed = now;
255       subtract_timeval (&elapsed, &_hurd_itimer_started);
256       remaining = _hurd_itimerval.it_value;
257       if (timercmp (&remaining, &elapsed, <))
258 	{
259 	  /* Hmm.  The timer should have just gone off, but has not been reset.
260 	     This is a possible timing glitch.  The alarm will signal soon. */
261 	  /* XXX wrong */
262 	  remaining.tv_sec = 0;
263 	  remaining.tv_usec = 0;
264 	}
265       else
266 	subtract_timeval (&remaining, &elapsed);
267 
268       /* Remember the old reload interval before changing it.  */
269       old_interval = _hurd_itimerval.it_interval;
270 
271       /* Record the starting time that the timer interval relates to.  */
272       _hurd_itimer_started = now;
273     }
274 
275   /* Load the new itimer value.  */
276   _hurd_itimerval = newval;
277 
278   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
279     {
280       /* Disable the itimer.  */
281       if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
282 	{
283 	  /* Suspend the itimer thread so it does nothing.  Then abort its
284 	     kernel context so that when the thread is resumed, mach_msg
285 	     will return to timer_thread (below) and it will fetch new
286 	     values from _hurd_itimerval.  */
287 	  if ((err = __thread_suspend (_hurd_itimer_thread))
288 	      || (err = __thread_abort (_hurd_itimer_thread)))
289 	    /* If we can't save it for later, nuke it.  */
290 	    kill_itimer_thread ();
291 	  else
292 	    _hurd_itimer_thread_suspended = 1;
293 	}
294     }
295   /* See if the timeout changed.  If so, we must alert the itimer thread.  */
296   else if (remaining.tv_sec != newval.it_value.tv_sec
297 	   || remaining.tv_usec != newval.it_value.tv_usec)
298     {
299       /* The timeout value is changing.  Tell the itimer thread to
300 	 reexamine it and start counting down.  If the itimer thread is
301 	 marked as suspended, either we just created it, or it was
302 	 suspended and thread_abort'd last time the itimer was disabled;
303 	 either way it will wake up and start waiting for the new timeout
304 	 value when we resume it.  If it is not suspended, the itimer
305 	 thread is waiting to deliver a pending alarm that we will override
306 	 (since it would come later than the new alarm being set);
307 	 thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it
308 	 will loop around and use the new timeout value.  */
309       if (err = (_hurd_itimer_thread_suspended
310 		 ? __thread_resume : __thread_abort) (_hurd_itimer_thread))
311 	{
312 	  kill_itimer_thread ();
313 	  goto out;
314 	}
315       _hurd_itimer_thread_suspended = 0;
316     }
317 
318   __spin_unlock (&_hurd_itimer_lock);
319   _hurd_critical_section_unlock (crit);
320 
321   if (old != NULL)
322     {
323       old->it_value = remaining;
324       old->it_interval = old_interval;
325     }
326   return 0;
327 
328  out:
329   __spin_unlock (&_hurd_itimer_lock);
330   _hurd_critical_section_unlock (crit);
331   return __hurd_fail (err);
332 }
333 
334 /* Set the timer WHICH to *NEW.  If OLD is not NULL,
335    set *OLD to the old value of timer WHICH.
336    Returns 0 on success, -1 on errors.  */
337 int
__setitimer(enum __itimer_which which,const struct itimerval * new,struct itimerval * old)338 __setitimer (enum __itimer_which which, const struct itimerval *new,
339 	     struct itimerval *old)
340 {
341   void *crit;
342   int ret;
343 
344   switch (which)
345     {
346     default:
347       return __hurd_fail (EINVAL);
348 
349     case ITIMER_VIRTUAL:
350     case ITIMER_PROF:
351       return __hurd_fail (ENOSYS);
352 
353     case ITIMER_REAL:
354       break;
355     }
356 
357 retry:
358   crit = _hurd_critical_section_lock ();
359   __spin_lock (&_hurd_itimer_lock);
360   ret = setitimer_locked (new, old, crit, 0);
361   if (ret == -1 && errno == EINTR)
362     /* Got a signal while inside an RPC of the critical section, retry again */
363     goto retry;
364 
365   return ret;
366 }
367 
368 static void
fork_itimer(void)369 fork_itimer (void)
370 {
371   /* We must restart the itimer in the child.  */
372 
373   struct itimerval it;
374 
375   __spin_lock (&_hurd_itimer_lock);
376   _hurd_itimer_thread = MACH_PORT_NULL;
377   it = _hurd_itimerval;
378   it.it_value = it.it_interval;
379 
380   setitimer_locked (&it, NULL, NULL, 0);
381 
382   (void) &fork_itimer;		/* Avoid gcc optimizing out the function.  */
383 }
384 text_set_element (_hurd_fork_child_hook, fork_itimer);
385 
386 weak_alias (__setitimer, setitimer)
387