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