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