1 /* ==========================================================================
2 * cqueues.h - Lua Continuation Queues
3 * --------------------------------------------------------------------------
4 * Copyright (c) 2012, 2014, 2015 William Ahern
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to permit
11 * persons to whom the Software is furnished to do so, subject to the
12 * following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
20 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23 * USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * ==========================================================================
25 */
26 #ifndef CQUEUES_H
27 #define CQUEUES_H
28
29 #include <signal.h> /* sigset_t */
30 #include <errno.h> /* EOVERFLOW */
31 #include <assert.h> /* static_assert */
32
33 #include <sys/param.h> /* __NetBSD_Version__ OpenBSD __FreeBSD__version */
34 #include <sys/types.h>
35 #include <sys/socket.h> /* socketpair(2) */
36 #include <unistd.h> /* close(2) pipe(2) */
37 #include <fcntl.h> /* F_GETFL F_SETFD F_SETFL FD_CLOEXEC O_NONBLOCK O_CLOEXEC fcntl(2) */
38
39 #include <lua.h>
40 #include <lualib.h>
41 #include <lauxlib.h>
42
43 #include "../vendor/compat53/c-api/compat-5.3.h"
44
45
46 /*
47 * F E A T U R E / E N V I R O N M E N T M A C R O S
48 *
49 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
50
51 #ifndef __has_feature
52 #define __has_feature(...) 0
53 #endif
54
55 #ifndef __has_extension
56 #define __has_extension(...) 0
57 #endif
58
59 #ifndef __NetBSD_Prereq__
60 #define __NetBSD_Prereq__(M, m, p) 0
61 #endif
62
63 #define GNUC_PREREQ(M, m) (defined __GNUC__ && ((__GNUC__ > M) || (__GNUC__ == M && __GNUC_MINOR__ >= m)))
64
65 #define NETBSD_PREREQ(M, m) __NetBSD_Prereq__(M, m, 0)
66
67 #define FREEBSD_PREREQ(M, m) (defined __FreeBSD_version && __FreeBSD_version >= ((M) * 100000) + ((m) * 1000))
68
69 #if defined __GLIBC_PREREQ
70 #define GLIBC_PREREQ(M, m) (defined __GLIBC__ && __GLIBC_PREREQ(M, m) && !__UCLIBC__)
71 #else
72 #define GLIBC_PREREQ(M, m) 0
73 #endif
74
75 #define UCLIBC_PREREQ(M, m, p) (defined __UCLIBC__ && (__UCLIBC_MAJOR__ > M || (__UCLIBC_MAJOR__ == M && __UCLIBC_MINOR__ > m) || (__UCLIBC_MAJOR__ == M && __UCLIBC_MINOR__ == m && __UCLIBC_SUBLEVEL__ >= p)))
76
77 #ifndef ENABLE_EPOLL
78 #define ENABLE_EPOLL HAVE_EPOLL_CREATE
79 #endif
80
81 #ifndef ENABLE_PORTS
82 #define ENABLE_PORTS HAVE_PORT_CREATE
83 #endif
84
85 #ifndef ENABLE_KQUEUE
86 #define ENABLE_KQUEUE HAVE_KQUEUE
87 #endif
88
89 #if __GNUC__
90 #define NOTUSED __attribute__((unused))
91 #define EXTENSION __extension__
92 #else
93 #define NOTUSED
94 #define EXTENSION
95 #endif
96
97 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) || __GNUC__ > 4 || __clang__
98 #define NOTREACHED __builtin_unreachable()
99 #else
100 #define NOTREACHED (void)0
101 #endif
102
103
104 /*
105 * C L A S S I N T E R F A C E S / R O U T I N E S
106 *
107 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
108
109 #define cqs_index_t int /* for documentation purposes */
110 #define cqs_nargs_t int /* "" */
111 #define cqs_error_t int /* "" */
112 #define cqs_status_t int /* "" */
113
114 #define CQS_CQUEUE "Continuation Queue"
115 #define CQS_SOCKET "CQS Socket"
116 #define CQS_SIGNAL "CQS Signal"
117 #define CQS_THREAD "CQS Thread"
118 #define CQS_NOTIFY "CQS Notify"
119 #define CQS_CONDITION "CQS Condition"
120
121 #ifndef CQS_USE_47BIT_LIGHTUSERDATA_HACK
122 /* LuaJIT only supports pointers with the low 47 bits set */
123 #if defined(LUA_JITLIBNAME) && (defined(_LP64) || defined(_LLP64) || defined(__arch64__) || defined (__arm64__) || defined (__aarch64__) || defined(_WIN64))
124 #define CQS_USE_47BIT_LIGHTUSERDATA_HACK 1
125 #else
126 #define CQS_USE_47BIT_LIGHTUSERDATA_HACK 0
127 #endif
128 #endif
129
130 #if CQS_USE_47BIT_LIGHTUSERDATA_HACK
131 #define CQS_UNIQUE_LIGHTUSERDATA_MASK(p) ((void *)((intptr_t)(p) & ((1UL<<47)-1)))
132 #else
133 #define CQS_UNIQUE_LIGHTUSERDATA_MASK(p) ((void *)(p))
134 #endif
135
136 #define CQUEUE__POLL CQS_UNIQUE_LIGHTUSERDATA_MASK(&cqueue__poll)
137
138 extern const char *cqueue__poll; // signals multilevel yield
139
140 cqs_nargs_t luaopen__cqueues(lua_State *);
141
142 cqs_nargs_t luaopen__cqueues_errno(lua_State *);
143
144 cqs_nargs_t luaopen__cqueues_socket(lua_State *);
145
146 cqs_nargs_t luaopen__cqueues_signal(lua_State *);
147
148 cqs_nargs_t luaopen__cqueues_thread(lua_State *);
149
150 cqs_nargs_t luaopen__cqueues_notify(lua_State *);
151
152 cqs_nargs_t luaopen__cqueues_condition(lua_State *);
153
154 cqs_nargs_t luaopen__cqueues_dns_record(lua_State *);
155
156 cqs_nargs_t luaopen__cqueues_dns_packet(lua_State *);
157
158 cqs_nargs_t luaopen__cqueues_dns_config(lua_State *);
159
160 cqs_nargs_t luaopen__cqueues_dns_hosts(lua_State *);
161
162 cqs_nargs_t luaopen__cqueues_dns_hints(lua_State *);
163
164 cqs_nargs_t luaopen__cqueues_dns_resolver(lua_State *);
165
166 cqs_nargs_t luaopen__cqueues_dns(lua_State *);
167
168
169 void cqs_cancelfd(lua_State *, int);
170
171
172 struct so_options;
173
174 cqs_error_t cqs_socket_fdopen(lua_State *, int, const struct so_options *);
175
176 int cqs_socket_pollfd(lua_State *, int);
177
178 int cqs_socket_events(lua_State *, int);
179
180 double cqs_socket_timeout(lua_State *, int);
181
182
cqs_requiref(lua_State * L,const char * modname,lua_CFunction openf,int glb)183 static void cqs_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb) {
184 luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
185 lua_getfield(L, -1, modname);
186 lua_remove(L, -2);
187
188 if (lua_isnil(L, -1)) {
189 lua_pop(L, 1);
190 luaL_requiref(L, modname, openf, glb);
191 }
192 } /* cqs_requiref() */
193
194
cqs_openlibs(lua_State * L)195 static void cqs_openlibs(lua_State *L) {
196 int top = lua_gettop(L);
197
198 cqs_requiref(L, "_cqueues", &luaopen__cqueues, 0);
199 cqs_requiref(L, "_cqueues.errno", &luaopen__cqueues_errno, 0);
200 cqs_requiref(L, "_cqueues.socket", &luaopen__cqueues_socket, 0);
201 cqs_requiref(L, "_cqueues.signal", &luaopen__cqueues_signal, 0);
202 cqs_requiref(L, "_cqueues.thread", &luaopen__cqueues_thread, 0);
203 cqs_requiref(L, "_cqueues.notify", &luaopen__cqueues_notify, 0);
204 #if 0 /* Make optional? */
205 cqs_requiref(L, "_cqueues.condition", &luaopen__cqueues_condition, 0);
206 cqs_requiref(L, "_cqueues.dns.record", &luaopen__cqueues_dns_record, 0);
207 cqs_requiref(L, "_cqueues.dns.packet", &luaopen__cqueues_dns_packet, 0);
208 cqs_requiref(L, "_cqueues.dns.config", &luaopen__cqueues_dns_config, 0);
209 cqs_requiref(L, "_cqueues.dns.hosts", &luaopen__cqueues_dns_hosts, 0);
210 cqs_requiref(L, "_cqueues.dns.hints", &luaopen__cqueues_dns_hints, 0);
211 cqs_requiref(L, "_cqueues.dns.resolver", &luaopen__cqueues_dns_resolver, 0);
212 cqs_requiref(L, "_cqueues.dns", &luaopen__cqueues_dns, 0);
213 #endif
214
215 lua_settop(L, top);
216 } /* cqs_openlibs() */
217
218
cqs_interpose(lua_State * L,const char * mt)219 static inline int cqs_interpose(lua_State *L, const char *mt) {
220 lua_settop(L, 2);
221
222 luaL_getmetatable(L, mt);
223 lua_getfield(L, -1, "__index");
224
225 lua_pushvalue(L, 1); /* push method name */
226 lua_gettable(L, -2); /* push old method */
227
228 lua_pushvalue(L, 1); /* push method name */
229 lua_pushvalue(L, 2); /* push new method */
230 lua_settable(L, -4); /* replace old method */
231
232 return 1; /* return old method */
233 } /* cqs_interpose() */
234
235
cqs_pushnils(lua_State * L,int n)236 static inline void cqs_pushnils(lua_State *L, int n) {
237 int i;
238
239 luaL_checkstack(L, n, "too many arguments");
240
241 for (i = 0; i < n; i++)
242 lua_pushnil(L);
243 } /* cqs_pushnils() */
244
245
cqs_regcount(const luaL_Reg * l)246 static inline int cqs_regcount(const luaL_Reg *l) {
247 int i;
248
249 for (i = 0; l[i].func; i++)
250 ;;
251
252 return i;
253 } /* cqs_regcount() */
254
255
256 /* create new metatable, capturing upvalues for use by methods and metamethods */
cqs_newmetatable(lua_State * L,const char * name,const luaL_Reg * methods,const luaL_Reg * metamethods,int nup)257 static inline void cqs_newmetatable(lua_State *L, const char *name, const luaL_Reg *methods, const luaL_Reg *metamethods, int nup) {
258 int i;
259
260 luaL_newmetatable(L, name);
261 for (i = 0; i < nup; i++) /* copy upvalues */
262 lua_pushvalue(L, -nup - 1);
263 luaL_setfuncs(L, metamethods, nup);
264
265 lua_createtable(L, 0, cqs_regcount(methods));
266 for (i = 0; i < nup; i++) /* copy upvalues */
267 lua_pushvalue(L, -nup - 2);
268 luaL_setfuncs(L, methods, nup);
269 lua_setfield(L, -2, "__index");
270
271 for (i = 0; i < nup; i++) /* remove the upvalues */
272 lua_remove(L, -2);
273 } /* cqs_newmetatable() */
274
275
276 /*
277 * set the n-th upvalue of every lua_CFunction in the table at tindex to the
278 * value at the top of the stack
279 */
cqs_setfuncsupvalue(lua_State * L,int tindex,int n)280 static inline void cqs_setfuncsupvalue(lua_State *L, int tindex, int n) {
281 tindex = lua_absindex(L, tindex);
282
283 lua_pushnil(L);
284 while (lua_next(L, tindex)) {
285 if (lua_iscfunction(L, -1)) {
286 lua_pushvalue(L, -3);
287 lua_setupvalue(L, -2, n);
288 }
289
290 lua_pop(L, 1); /* pop field value (leaving key) */
291 }
292
293 lua_pop(L, 1); /* pop upvalue */
294 } /* cqs_setfuncsupvalue() */
295
296
cqs_setmetaupvalue(lua_State * L,int tindex,int n)297 static inline void cqs_setmetaupvalue(lua_State *L, int tindex, int n) {
298 tindex = lua_absindex(L, tindex);
299
300 lua_pushvalue(L, -1);
301 cqs_setfuncsupvalue(L, tindex, n);
302
303 lua_getfield(L, tindex, "__index");
304 lua_pushvalue(L, -2);
305 cqs_setfuncsupvalue(L, -2, n);
306 lua_pop(L, 1); /* pop __index */
307
308 lua_pop(L, 1); /* pop upvalue */
309 } /* cqs_setmetaupvalue() */
310
311
312 /* test metatable against copy at upvalue */
cqs_testudata(lua_State * L,int index,int upvalue)313 static inline void *cqs_testudata(lua_State *L, int index, int upvalue) {
314 void *ud = lua_touserdata(L, index);
315 int eq;
316
317 if (!ud || !lua_getmetatable(L, index))
318 return NULL;
319
320 eq = lua_rawequal(L, -1, lua_upvalueindex(upvalue));
321 lua_pop(L, 1);
322
323 return (eq)? ud : NULL;
324 } /* cqs_testudata() */
325
326
cqs_checkudata(lua_State * L,int index,int upvalue,const char * tname)327 static inline void *cqs_checkudata(lua_State *L, int index, int upvalue, const char *tname) {
328 void *ud;
329
330 if (!(ud = cqs_testudata(L, index, upvalue))) {
331 index = lua_absindex(L, index);
332
333 luaL_argerror(L, index, lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, index)));
334
335 NOTREACHED;
336 }
337
338 return ud;
339 } /* cqs_checkudata() */
340
341
342 struct cqs_macro { const char *name; int value; };
343
cqs_setmacros(lua_State * L,int index,const struct cqs_macro * macro,size_t count,_Bool swap)344 static inline void cqs_setmacros(lua_State *L, int index, const struct cqs_macro *macro, size_t count, _Bool swap) {
345 index = lua_absindex(L, index);
346
347 for (unsigned i = 0; i < count; i++) {
348 lua_pushstring(L, macro[i].name);
349 lua_pushinteger(L, macro[i].value);
350 lua_rawset(L, index);
351 }
352
353 if (!swap)
354 return;
355
356 for (unsigned i = 0; i < count; i++) {
357 lua_pushinteger(L, macro[i].value);
358 lua_pushstring(L, macro[i].name);
359 lua_rawset(L, index);
360 }
361 } /* cqs_setmacros() */
362
363
364 #if LUA_VERSION_NUM < 503
365 /* convert value at index to proxytable with value at t[2] */
cqs__toproxytable(lua_State * L,int index)366 static inline void cqs__toproxytable(lua_State *L, int index) {
367 index = lua_absindex(L, index);
368 lua_createtable(L, 2, 0);
369 lua_pushlightuserdata(L, (void *)lua_topointer(L, -1));
370 lua_rawseti(L, -2, 1); /* set t[1] == pointer-to-t */
371 lua_pushvalue(L, index);
372 lua_rawseti(L, -2, 2); /* set t[2] == value */
373 lua_replace(L, index);
374 } /* cqs__toproxytable() */
375
376 /* check whether value at index is a proxytable */
cqs__isproxytable(lua_State * L,int index)377 static inline _Bool cqs__isproxytable(lua_State *L, int index) {
378 const void *tp, *t1p;
379
380 if (!lua_istable(L, index))
381 return 0;
382
383 tp = lua_topointer(L, index);
384 lua_rawgeti(L, index, 1);
385 t1p = lua_topointer(L, -1);
386 lua_pop(L, 1);
387
388 return tp && tp == t1p;
389 } /* cqs__isproxytable() */
390 #endif
391
cqs_setuservalue(lua_State * L,int index)392 static inline void cqs_setuservalue(lua_State *L, int index) {
393 #if LUA_VERSION_NUM >= 503
394 lua_setuservalue(L, index);
395 #elif LUA_VERSION_NUM == 502
396 if (!lua_istable(L, -1) && !lua_isnil(L, -1))
397 cqs__toproxytable(L, -1);
398 lua_setuservalue(L, index);
399 #else
400 if (!lua_istable(L, -1))
401 cqs__toproxytable(L, -1);
402 lua_setfenv(L, index);
403 #endif
404 } /* cqs_setuservalue() */
405
cqs_getuservalue(lua_State * L,int index)406 static inline int cqs_getuservalue(lua_State *L, int index) {
407 #if LUA_VERSION_NUM >= 503
408 return lua_getuservalue(L, index);
409 #else
410 lua_getuservalue(L, index);
411 if (cqs__isproxytable(L, -1)) {
412 lua_rawgeti(L, -1, 2);
413 lua_replace(L, -2);
414 }
415 return lua_type(L, -1);
416 #endif
417 } /* cqs_setuservalue() */
418
419
cqs_closefd(int * fd)420 static inline void cqs_closefd(int *fd) {
421 if (*fd != -1) {
422 #if __APPLE__
423 /* Do we need bother with close$NOCANCEL$UNIX2003? */
424 extern int close$NOCANCEL(int);
425 close$NOCANCEL(*fd);
426 #else
427 close(*fd);
428 #endif
429 *fd = -1;
430 }
431 } /* cqs_closefd() */
432
433
434 #if !defined O_CLOEXEC
435 #if __NetBSD__ /* bad hack for NetBSD < 6.0 until we refactor flags code */
436 #define O_CLOEXEC 0x00400000
437 #endif
438 #endif
439
cqs_setfd(int fd,int flags)440 static inline int cqs_setfd(int fd, int flags) {
441 if (flags & O_NONBLOCK) {
442 int oflags = fcntl(fd, F_GETFL);
443 if (-1 == oflags || -1 == fcntl(fd, F_SETFL, oflags|O_NONBLOCK))
444 return errno;
445 }
446
447 if (flags & O_CLOEXEC) {
448 if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC))
449 return errno;
450 }
451
452 return 0;
453 } /* cqs_setfd() */
454
455
cqs_pipe(int fd[2],int flags)456 static inline int cqs_pipe(int fd[2], int flags) {
457 #if HAVE_PIPE2
458 if (0 != pipe2(fd, flags))
459 return errno;
460
461 return 0;
462 #else
463 int error;
464
465 if (0 != pipe(fd))
466 return errno;
467
468 if ((error = cqs_setfd(fd[0], flags)) || (error = cqs_setfd(fd[1], flags)))
469 return error;
470
471 return 0;
472 #endif
473 } /* cqs_pipe() */
474
475
cqs_socketpair(int family,int type,int proto,int fd[2],int flags)476 static inline int cqs_socketpair(int family, int type, int proto, int fd[2], int flags) {
477 #if defined SOCK_NONBLOCK && defined SOCK_CLOEXEC
478 if (flags & O_NONBLOCK)
479 type |= SOCK_NONBLOCK;
480 if (flags & O_CLOEXEC)
481 type |= SOCK_CLOEXEC;
482
483 if (0 != socketpair(family, type, proto, fd))
484 return errno;
485
486 return 0;
487 #else
488 int error;
489
490 if (0 != socketpair(family, type, proto, fd))
491 return errno;
492
493 if ((error = cqs_setfd(fd[0], flags)) || (error = cqs_setfd(fd[1], flags)))
494 return error;
495
496 return 0;
497 #endif
498 } /* cqs_socketpair() */
499
500
501 #if HAVE_STATIC_ASSERT
502 #define cqs_static_assert(cond, msg) static_assert(cond, msg)
503 #elif HAVE__STATIC_ASSERT
504 #define cqs_static_assert(cond, msg) EXTENSION _Static_assert(cond, msg)
505 #else
506 #define cqs_inline_assert(cond) (sizeof (int[1 - 2*!(cond)]))
507 #define cqs_static_assert(cond, msg) extern char CQS_XPASTE(assert_, __LINE__)[cqs_inline_assert(cond)]
508 #endif
509
510
511 cqs_error_t cqs_strerror_r(cqs_error_t, char *, size_t);
512
513 /*
514 * NB: Compound literals have block scope in C. But g++ creates
515 * list-initialized temporaries, which only have expression scope.
516 */
517 #if !__cplusplus
518 #define cqs_strerror(...) cqs_strerror_(__VA_ARGS__, (char [128]){ 0 }, 128, 0)
519 #define cqs_strerror_(error, dst, lim, ...) (cqs_strerror)((error), (dst), (lim))
520 #endif
521
522 const char *(cqs_strerror)(cqs_error_t, void *, size_t);
523
524
525 cqs_error_t cqs_sigmask(int, const sigset_t *, sigset_t *);
526
527
528 /*
529 * A U X I L L A R Y R O U T I N E S
530 *
531 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
532
533 #ifndef MIN
534 #define MIN(a, b) (((a) < (b))? (a) : (b))
535 #endif
536
537 #ifndef MAX
538 #define MAX(a, b) (((a) > (b))? (a) : (b))
539 #endif
540
541 #ifndef countof
542 #define countof(a) (sizeof (a) / sizeof *(a))
543 #endif
544
545 #ifndef endof
546 #define endof(a) (&(a)[countof(a)])
547 #endif
548
549 #define cqs_ispowerof2(x) (((x) != 0) && (0 == (((x) - 1) & (x))))
550
551 #define CQS_PASTE(x, y) x ## y
552 #define CQS_XPASTE(x, y) CQS_PASTE(x, y)
553
554 typedef int cqs_ref_t;
555
cqs_unref(lua_State * L,cqs_ref_t * ref)556 static inline void cqs_unref(lua_State *L, cqs_ref_t *ref) {
557 if (*ref != LUA_NOREF) {
558 luaL_unref(L, LUA_REGISTRYINDEX, *ref);
559 *ref = LUA_NOREF;
560 }
561 } /* cqs_unref() */
562
cqs_ref(lua_State * L,cqs_ref_t * ref)563 static inline void cqs_ref(lua_State *L, cqs_ref_t *ref) {
564 cqs_unref(L, ref);
565 *ref = luaL_ref(L, LUA_REGISTRYINDEX);
566 } /* cqs_ref() */
567
cqs_getref(lua_State * L,cqs_ref_t ref)568 static inline void cqs_getref(lua_State *L, cqs_ref_t ref) {
569 if (ref != LUA_NOREF)
570 lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
571 else
572 lua_pushnil(L);
573 } /* cqs_getref() */
574
575
cqs_addzu(size_t * r,size_t a,size_t b)576 static inline cqs_error_t cqs_addzu(size_t *r, size_t a, size_t b) {
577 if (~a < b)
578 return EOVERFLOW;
579
580 *r = a + b;
581
582 return 0;
583 } /* cqs_addzu() */
584
585
586 /*
587 * D E B U G M A C R O S
588 *
589 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
590
591 #if !defined SAY
592 #define SAY_(file, func, line, fmt, ...) \
593 fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__)
594
595 #define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n")
596
597 #define HAI SAY("hai")
598 #endif
599
600 #include <string.h>
601
602 #include <sys/stat.h>
603 #include <sys/ioctl.h>
604
605 #if __sun
606 #include <sys/filio.h>
607 #include <stropts.h>
608 #endif
609
cqs_debugfd(int fd)610 NOTUSED static void cqs_debugfd(int fd) {
611 struct stat st;
612 char descr[64] = "";
613 int pending = -1;
614
615 if (0 != fstat(fd, &st))
616 goto syerr;
617
618 if (S_ISSOCK(st.st_mode)) {
619 int type;
620
621 if (0 != getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &(socklen_t){ sizeof type }))
622 goto syerr;
623
624 if (type == SOCK_STREAM)
625 strncat(descr, "stream socket", sizeof descr - 1);
626 else if (type == SOCK_DGRAM)
627 strncat(descr, "dgram socket", sizeof descr - 1);
628 else
629 strncat(descr, "other socket", sizeof descr - 1);
630 } else {
631 if (S_ISFIFO(st.st_mode))
632 strncat(descr, "fifo file", sizeof descr - 1);
633 else if (S_ISREG(st.st_mode))
634 strncat(descr, "regular file", sizeof descr - 1);
635 else
636 strncat(descr, "other file", sizeof descr - 1);
637 }
638
639 ioctl(fd, FIONREAD, &pending);
640
641 SAY("%d: %s (pending:%d)", fd, descr, pending);
642
643 return;
644 syerr:
645 SAY("%d: %s", fd, strerror(errno));
646 } /* cqs_debugfd() */
647
648
649 #endif /* CQUEUES_H */
650