1 /* Report on what a thread in our task is waiting for.
2    Copyright (C) 1996-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
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, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <hurd.h>
20 #include <hurd/signal.h>
21 #include <hurd/fd.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <hurd/msg_server.h>
25 #include <thread_state.h>
26 #include <intr-msg.h>
27 
28 static char *
describe_number(char * description,const char * flavor,long int i,size_t size)29 describe_number (char *description, const char *flavor, long int i, size_t size)
30 {
31   unsigned long int j;
32   char *limit = description + size;
33   char *p = flavor == NULL ? description : __stpncpy (description, flavor, size);
34   char *end;
35 
36   if (p == limit)
37     return p;
38 
39   /* Handle sign.  */
40   if (i < 0)
41     {
42       i = -i;
43       *p++ = '-';
44     }
45 
46   if (p == limit)
47     return p;
48 
49   /* Allocate space for the number at the end of DESCRIPTION.  */
50   for (j = i; j >= 10; j /= 10)
51     p++;
52   end = p + 1;
53 
54   if (end < limit)
55     *end = '\0';
56   else
57     end = limit;
58 
59   do
60     {
61       if (p < limit)
62 	*p = '0' + i % 10;
63       p--;
64       i /= 10;
65     } while (i != 0);
66 
67   return end;
68 }
69 
70 static char *
describe_port(char * description,mach_port_t port,size_t size)71 describe_port (char *description, mach_port_t port, size_t size)
72 {
73   int i;
74 
75   if (port == MACH_PORT_NULL)
76     return __stpncpy (description, "(null)", size);
77   if (port == MACH_PORT_DEAD)
78     return __stpncpy (description, "(dead)", size);
79 
80   if (port == __mach_task_self ())
81     return __stpncpy (description, "task-self", size);
82 
83   for (i = 0; i < _hurd_nports; ++i)
84     if (port == _hurd_ports[i].port)
85       return describe_number (description, "init#", i, size);
86 
87   if (_hurd_init_dtable)
88     {
89       for (i = 0; i < _hurd_init_dtablesize; ++i)
90 	if (port == _hurd_init_dtable[i])
91 	  return describe_number (description, "fd#", i, size);
92     }
93   if (_hurd_dtable)
94     {
95       for (i = 0; i < _hurd_dtablesize; ++i)
96 	if (_hurd_dtable[i] == NULL)
97 	  continue;
98 	else if (port == _hurd_dtable[i]->port.port)
99 	  return describe_number (description, "fd#", i, size);
100 	else if (port == _hurd_dtable[i]->ctty.port)
101 	  return describe_number (description, "bgfd#", i, size);
102     }
103 
104   return describe_number (description, "port#", port, size);
105 }
106 
107 
108 /* We want _HURD_ITIMER_THREAD, but don't want to link in the itimer code
109    unnecessarily.  */
110 extern thread_t _hurd_itimer_thread; /* XXX */
weak_extern(_hurd_itimer_thread)111 weak_extern (_hurd_itimer_thread)
112 
113 kern_return_t
114 _S_msg_report_wait (mach_port_t msgport, thread_t thread,
115 		    string_t description, mach_msg_id_t *msgid)
116 {
117   char *limit = description + 1024; /* XXX */
118   char *cur = description;
119   *msgid = 0;
120 
121   if (thread == _hurd_msgport_thread)
122     /* Cute.  */
123     cur = __stpncpy (cur, "msgport", limit - cur);
124   else if (&_hurd_itimer_thread && thread == _hurd_itimer_thread)
125     cur = __stpncpy (cur, "itimer", limit - cur);
126   else
127     {
128       /* Make sure this is really one of our threads.  */
129 
130       struct hurd_sigstate *ss;
131 
132       __mutex_lock (&_hurd_siglock);
133       for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
134 	if (ss->thread == thread)
135 	  break;
136       __mutex_unlock (&_hurd_siglock);
137       if (ss == NULL)
138 	/* To hell with you.  */
139 	return EINVAL;
140 
141       if (ss->suspended != MACH_PORT_NULL)
142 	cur = __stpncpy (cur, "sigsuspend", limit - cur);
143       else
144 	{
145 	  /* Examine the thread's state to see if it is blocked in an RPC.  */
146 
147 	  struct machine_thread_state state;
148 	  mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
149 	  error_t err;
150 
151 	  err = __thread_get_state (thread, MACHINE_THREAD_STATE_FLAVOR,
152 				    (natural_t *) &state, &count);
153 	  if (err)
154 	    return err;
155 	  assert (count == MACHINE_THREAD_STATE_COUNT);
156 	  if (SYSCALL_EXAMINE (&state, msgid))
157 	    {
158 	      mach_port_t send_port, rcv_port;
159 	      mach_msg_option_t option;
160 	      mach_msg_timeout_t timeout;
161 
162 	      /* Blocked in a system call.  */
163 	      if (*msgid == -25
164 		  /* mach_msg system call.  Examine its parameters.  */
165 		  && MSG_EXAMINE (&state, msgid, &rcv_port, &send_port,
166 				  &option, &timeout) == 0)
167 		{
168 		  if (send_port != MACH_PORT_NULL && *msgid != 0)
169 		    {
170 		      /* For the normal case of RPCs, we consider the
171 			 destination port to be the interesting thing
172 			 whether we are in fact sending or receiving at the
173 			 moment.  That tells us who we are waiting for the
174 			 reply from.  */
175 		      if (send_port == ss->intr_port)
176 			{
177 			  /* This is a Hurd interruptible RPC.
178 			     Mark it by surrounding the port description
179 			     string with [...] brackets.  */
180 			  if (cur < limit)
181 			    *cur++ = '[';
182 			  cur = describe_port (cur, send_port, limit - cur);
183 			  if (cur < limit)
184 			    *cur++ = ']';
185 			}
186 		      else
187 			cur = describe_port (cur, send_port, limit - cur);
188 		    }
189 		  else if (rcv_port != MACH_PORT_NULL)
190 		    {
191 		      /* This system call had no send port, but had a
192 			 receive port.  The msgid we extracted is then just
193 			 some garbage or perhaps the msgid of the last
194 			 message this thread received, but it's not a
195 			 helpful thing to return.  */
196 		      cur = describe_port (cur, rcv_port, limit - cur);
197 		      cur = __stpncpy (cur, ":rcv", limit - cur);
198 		      *msgid = 0;
199 		    }
200 		  else if ((option & (MACH_RCV_MSG|MACH_RCV_TIMEOUT))
201 			   == (MACH_RCV_MSG|MACH_RCV_TIMEOUT))
202 		    {
203 		      /* A receive with no valid port can be used for a
204 			 pure timeout.  Report the timeout value (counted
205 			 in milliseconds); note this is the original total
206 			 time, not the time remaining.  */
207 		      cur = describe_number (cur, 0, timeout, limit - cur);
208 		      cur = __stpncpy (cur, "ms", limit - cur);
209 		      *msgid = 0;
210 		    }
211 		  else
212 		    {
213 		      cur = __stpncpy (cur, "mach_msg", limit - cur);
214 		      *msgid = 0;
215 		    }
216 		}
217 	      else		/* Some other system call.  */
218 		{
219 		  cur = describe_number (cur, "syscall#", *msgid, limit - cur);
220 		  *msgid = 0;
221 		}
222 	    }
223 	}
224     }
225 
226   __mach_port_deallocate (__mach_task_self (), thread);
227 
228   if (cur == limit)
229     return ENOMEM;
230 
231   *cur = '\0';
232   return 0;
233 }
234 
235 kern_return_t
_S_msg_describe_ports(mach_port_t msgport,mach_port_t refport,const mach_port_t * ports,mach_msg_type_number_t nports,char ** desc,mach_msg_type_number_t * desclen)236 _S_msg_describe_ports (mach_port_t msgport, mach_port_t refport,
237 		       const mach_port_t *ports, mach_msg_type_number_t nports,
238 		       char **desc, mach_msg_type_number_t *desclen)
239 {
240   char *p, *end;
241 
242   if (__USEPORT (AUTH, msgport != port))
243     return EPERM;
244 
245   end = *desc + *desclen;
246   p = *desc;
247   while (nports-- > 0)
248     {
249       char this[200];
250       describe_port (this, *ports++, sizeof this);
251       p = __stpncpy (p, this, end - p);
252       if (p == end && p[-1] != '\0')
253 	return ENOMEM;
254     }
255 
256   *desclen = p - *desc;
257   return 0;
258 }
259