codegen: add a 'size' argument to ALU_WRITES_FLAGS
[ajla.git] / os_os2.c
blobab0ad7e7f62783df83a4cb6841a5cbf26c96f177
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #ifdef OS_OS2
23 #include "str.h"
24 #include "list.h"
25 #include "tree.h"
26 #include "thread.h"
27 #include "addrlock.h"
28 #include "obj_reg.h"
29 #include "os_util.h"
31 #include "os.h"
32 #include "iomux.h"
34 #define TCPIPV4
35 #define MAXSOCKETS 2048
37 #include <stdio.h>
38 #include <time.h>
39 #include <sys/socket.h>
40 #include <sys/so_ioctl.h>
41 #include <netinet/in.h>
42 #include <sys/un.h>
43 #include <netdb.h>
45 #define OS2_PIPE_SIZE 4096
46 #define OS2_BUFFER_SIZE 4096
47 #define OS2_PACKET_BUFFER_SIZE 4096
48 #define OS2_IO_THREAD_STACK_SIZE 65536
49 #define OS2_MAX_HANDLE 32768
51 #ifndef wake_up_wait_list
52 void u_name(wake_up_wait_list)(struct list *wait_list, mutex_t *mutex_to_lock, bool can_allocate_memory);
53 void c_name(wake_up_wait_list)(struct list *wait_list, mutex_t *mutex_to_lock, bool can_allocate_memory);
54 #endif
56 struct os2_io_thread {
57 TID thread;
58 HEV event;
59 char *buffer;
60 size_t buffer_pos;
61 size_t buffer_len;
62 APIRET err;
63 bool eof;
64 bool should_close;
65 bool packet_mode;
66 bool packet_is_queued;
67 unsigned char last_buttons;
68 bool fullscreen;
69 HMOU mh;
70 struct console_read_packet pkt;
71 struct list wait_list;
72 struct list socket_entry;
73 handle_t h;
76 struct os2_handle {
77 HFILE h;
78 bool packet_mode;
79 unsigned char t;
80 char disk;
81 int flags;
83 struct os2_io_thread rd;
84 struct os2_io_thread ms;
85 struct os2_io_thread wr;
87 struct list deferred_entry;
90 #define HANDTYPE_SOCKET 0xff
92 #define SOCKADDR_MAX_LEN 512
93 #define SOCKADDR_ALIGN 16
95 #include "os_os2_e.inc"
96 #include "os_com.inc"
98 #define A_DECL(type, var) type var##1, var##2, *var = _THUNK_PTR_STRUCT_OK(&var##1) ? &var##1 : &var##2
100 static bool os_threads_initialized;
102 static unsigned n_std_handles;
103 static handle_t *os2_std_handles;
105 dir_handle_t os_cwd;
107 static struct list deferred_write_list;
108 static struct list deferred_closed_list;
109 static mutex_t deferred_mutex;
111 static struct list socket_list[2];
112 static mutex_t socket_list_mutex;
114 static long double freq_period_usec;
115 static APIRET (*proc_DosTmrQueryFreq)(PULONG) = NULL;
116 static APIRET (*proc_DosTmrQueryTime)(PQWORD) = NULL;
117 static APIRET (*proc_DosOpenL)(PSZ, PHFILE, PULONG, long long, ULONG, ULONG, ULONG, PEAOP2) = NULL;
118 static APIRET (*proc_DosSetFilePtrL)(HFILE, long long, ULONG, long long *);
119 static APIRET (*proc_DosSetFileSizeL)(HFILE, long long);
121 static bool tcpip_loaded = false;
123 static int (*proc_accept)(int, struct sockaddr *, int *);
124 static int (*proc_bind)(int, struct sockaddr *, int);
125 static int (*proc_connect)(int, struct sockaddr *, int);
126 static void *(*proc_gethostbyname)(const char *);
127 static int (*proc_gethostname)(char *, int);
128 static int (*proc_getpeername)(int, struct sockaddr *, int *);
129 static int (*proc_getsockname)(int, struct sockaddr *, int *);
130 static int (*proc_getsockopt)(int, int, int, void *, int *);
131 static int (*proc_ioctl)(int, int, void *, int);
132 static int (*proc_listen)(int, int);
133 static int (*proc_recv)(int, void *, int, int);
134 static int (*proc_recvfrom)(int, void *, int, int, struct sockaddr *, int *);
135 static int (*proc_select)(int *, int, int, int, long);
136 static int (*proc_send)(int, const void *, int, int);
137 static int (*proc_sendto)(int, const void *, int, int, const struct sockaddr *, int);
138 static int (*proc_setsockopt)(int, int, int, const void *, int);
139 static int (*proc_shutdown)(int, int);
140 static int (*proc_socket)(int domain, int type, int protocol);
141 static int (*proc_sock_errno)(void);
142 static int (*proc_sock_init)(void);
143 static int (*proc_soclose)(int handle);
144 static int *proc_h_errno;
147 struct system_error_table_entry {
148 unsigned short errn;
149 unsigned short sys_error;
152 static const struct system_error_table_entry os2_error_to_system_error[] = {
153 { ERROR_FILE_NOT_FOUND, SYSTEM_ERROR_ENOENT },
154 { ERROR_PATH_NOT_FOUND, SYSTEM_ERROR_ENOENT },
155 { ERROR_TOO_MANY_OPEN_FILES, SYSTEM_ERROR_EMFILE },
156 { ERROR_ACCESS_DENIED, SYSTEM_ERROR_EACCES },
157 { ERROR_INVALID_HANDLE, SYSTEM_ERROR_EBADF },
158 { ERROR_NOT_ENOUGH_MEMORY, SYSTEM_ERROR_ENOMEM },
159 { ERROR_INVALID_DRIVE, SYSTEM_ERROR_ENOENT },
160 { ERROR_CURRENT_DIRECTORY, SYSTEM_ERROR_EBUSY },
161 { ERROR_NOT_SAME_DEVICE, SYSTEM_ERROR_EXDEV },
162 { ERROR_WRITE_PROTECT, SYSTEM_ERROR_EROFS },
163 { ERROR_CRC, SYSTEM_ERROR_EIO },
164 { ERROR_SEEK, SYSTEM_ERROR_EIO },
165 { ERROR_NOT_DOS_DISK, SYSTEM_ERROR_EMEDIUMTYPE },
166 { ERROR_SECTOR_NOT_FOUND, SYSTEM_ERROR_EIO },
167 { ERROR_WRITE_FAULT, SYSTEM_ERROR_EIO },
168 { ERROR_READ_FAULT, SYSTEM_ERROR_EIO },
169 { ERROR_GEN_FAILURE, SYSTEM_ERROR_EIO },
170 { ERROR_SHARING_VIOLATION, SYSTEM_ERROR_EBUSY },
171 { ERROR_WRONG_DISK, SYSTEM_ERROR_EMEDIUMTYPE },
172 { ERROR_HANDLE_DISK_FULL, SYSTEM_ERROR_ENOSPC },
173 { ERROR_SBCS_ATT_WRITE_PROT, SYSTEM_ERROR_EROFS },
174 { ERROR_FILE_EXISTS, SYSTEM_ERROR_EEXIST },
175 { ERROR_CANNOT_MAKE, SYSTEM_ERROR_ENOENT },
176 { ERROR_INTERRUPT, SYSTEM_ERROR_EINTR },
177 { ERROR_DEVICE_IN_USE, SYSTEM_ERROR_EBUSY },
178 { ERROR_DRIVE_LOCKED, SYSTEM_ERROR_EBUSY },
179 { ERROR_BROKEN_PIPE, SYSTEM_ERROR_EPIPE },
180 { ERROR_OPEN_FAILED, SYSTEM_ERROR_ENOENT },
181 { ERROR_DISK_FULL, SYSTEM_ERROR_ENOSPC },
182 { ERROR_NO_MORE_SEARCH_HANDLES, SYSTEM_ERROR_ENFILE },
183 { ERROR_INVALID_NAME, SYSTEM_ERROR_ENOENT },
184 { ERROR_SEEK_ON_DEVICE, SYSTEM_ERROR_ESPIPE },
185 { ERROR_BAD_PATHNAME, SYSTEM_ERROR_ENOENT },
186 { ERROR_FILENAME_EXCED_RANGE, SYSTEM_ERROR_ENAMETOOLONG },
187 { ERROR_PIPE_BUSY, SYSTEM_ERROR_EBUSY },
188 { ERROR_NO_DATA, SYSTEM_ERROR_EAGAIN },
189 { ERROR_PIPE_NOT_CONNECTED, SYSTEM_ERROR_ENOTCONN },
190 { ERROR_CIRCULARITY_REQUESTED, SYSTEM_ERROR_EINVAL },
191 { ERROR_DIRECTORY_IN_CDS, SYSTEM_ERROR_EBUSY },
192 { ERROR_INVALID_PATH, SYSTEM_ERROR_ENOENT },
193 { ERROR_TOO_MANY_OPENS, SYSTEM_ERROR_EMFILE },
196 static ajla_error_t error_from_os2(int ec, APIRET rc)
198 size_t r;
199 binary_search(size_t, n_array_elements(os2_error_to_system_error), r, os2_error_to_system_error[r].errn == rc, os2_error_to_system_error[r].errn < rc, return error_ajla_aux(ec, AJLA_ERROR_OS2, rc));
200 return error_ajla_aux(ec, AJLA_ERROR_SYSTEM, os2_error_to_system_error[r].sys_error);
203 static const struct system_error_table_entry socket_error_to_system_error[] = {
204 { SOCEPERM, SYSTEM_ERROR_EPERM },
205 { SOCENOENT, SYSTEM_ERROR_ENOENT },
206 { SOCESRCH, SYSTEM_ERROR_ESRCH },
207 { SOCEINTR, SYSTEM_ERROR_EINTR },
208 { SOCEIO, SYSTEM_ERROR_EIO },
209 { SOCENXIO, SYSTEM_ERROR_ENXIO },
210 { SOCE2BIG, SYSTEM_ERROR_E2BIG },
211 { SOCENOEXEC, SYSTEM_ERROR_ENOEXEC },
212 { SOCEBADF, SYSTEM_ERROR_EBADF },
213 { SOCECHILD, SYSTEM_ERROR_ECHILD },
214 { SOCEDEADLK, SYSTEM_ERROR_EDEADLK },
215 { SOCENOMEM, SYSTEM_ERROR_ENOMEM },
216 { SOCEACCES, SYSTEM_ERROR_EACCES },
217 { SOCEFAULT, SYSTEM_ERROR_EFAULT },
218 { SOCENOTBLK, SYSTEM_ERROR_ENOTBLK },
219 { SOCEBUSY, SYSTEM_ERROR_EBUSY },
220 { SOCEEXIST, SYSTEM_ERROR_EEXIST },
221 { SOCEXDEV, SYSTEM_ERROR_EXDEV },
222 { SOCENODEV, SYSTEM_ERROR_ENODEV },
223 { SOCENOTDIR, SYSTEM_ERROR_ENOTDIR },
224 { SOCEISDIR, SYSTEM_ERROR_EISDIR },
225 { SOCEINVAL, SYSTEM_ERROR_EINVAL },
226 { SOCENFILE, SYSTEM_ERROR_ENFILE },
227 { SOCEMFILE, SYSTEM_ERROR_EMFILE },
228 { SOCENOTTY, SYSTEM_ERROR_ENOTTY },
229 { SOCETXTBSY, SYSTEM_ERROR_ETXTBSY },
230 { SOCEFBIG, SYSTEM_ERROR_EFBIG },
231 { SOCENOSPC, SYSTEM_ERROR_ENOSPC },
232 { SOCESPIPE, SYSTEM_ERROR_ESPIPE },
233 { SOCEROFS, SYSTEM_ERROR_EROFS },
234 { SOCEMLINK, SYSTEM_ERROR_EMLINK },
235 { SOCEPIPE, SYSTEM_ERROR_EPIPE },
236 { SOCEDOM, SYSTEM_ERROR_EDOM },
237 { SOCERANGE, SYSTEM_ERROR_ERANGE },
238 { SOCEAGAIN, SYSTEM_ERROR_EAGAIN },
239 { SOCEINPROGRESS, SYSTEM_ERROR_EINPROGRESS },
240 { SOCEALREADY, SYSTEM_ERROR_EALREADY },
241 { SOCENOTSOCK, SYSTEM_ERROR_ENOTSOCK },
242 { SOCEDESTADDRREQ, SYSTEM_ERROR_EDESTADDRREQ },
243 { SOCEMSGSIZE, SYSTEM_ERROR_EMSGSIZE },
244 { SOCEPROTOTYPE, SYSTEM_ERROR_EPROTOTYPE },
245 { SOCENOPROTOOPT, SYSTEM_ERROR_ENOPROTOOPT },
246 { SOCEPROTONOSUPPORT, SYSTEM_ERROR_EPROTONOSUPPORT },
247 { SOCESOCKTNOSUPPORT, SYSTEM_ERROR_ESOCKTNOSUPPORT },
248 { SOCEOPNOTSUPP, SYSTEM_ERROR_EOPNOTSUPP },
249 { SOCEPFNOSUPPORT, SYSTEM_ERROR_EPFNOSUPPORT },
250 { SOCEAFNOSUPPORT, SYSTEM_ERROR_EAFNOSUPPORT },
251 { SOCEADDRINUSE, SYSTEM_ERROR_EADDRINUSE },
252 { SOCEADDRNOTAVAIL, SYSTEM_ERROR_EADDRNOTAVAIL },
253 { SOCENETDOWN, SYSTEM_ERROR_ENETDOWN },
254 { SOCENETUNREACH, SYSTEM_ERROR_ENETUNREACH },
255 { SOCENETRESET, SYSTEM_ERROR_ENETRESET },
256 { SOCECONNABORTED, SYSTEM_ERROR_ECONNABORTED },
257 { SOCECONNRESET, SYSTEM_ERROR_ECONNRESET },
258 { SOCENOBUFS, SYSTEM_ERROR_ENOBUFS },
259 { SOCEISCONN, SYSTEM_ERROR_EISCONN },
260 { SOCENOTCONN, SYSTEM_ERROR_ENOTCONN },
261 { SOCESHUTDOWN, SYSTEM_ERROR_ESHUTDOWN },
262 { SOCETOOMANYREFS, SYSTEM_ERROR_ETOOMANYREFS },
263 { SOCETIMEDOUT, SYSTEM_ERROR_ETIMEDOUT },
264 { SOCECONNREFUSED, SYSTEM_ERROR_ECONNREFUSED },
265 { SOCELOOP, SYSTEM_ERROR_ELOOP },
266 { SOCENAMETOOLONG, SYSTEM_ERROR_ENAMETOOLONG },
267 { SOCEHOSTDOWN, SYSTEM_ERROR_EHOSTDOWN },
268 { SOCEHOSTUNREACH, SYSTEM_ERROR_EHOSTUNREACH },
269 { SOCENOTEMPTY, SYSTEM_ERROR_ENOTEMPTY },
270 /* { SOCEPROCLIM, SYSTEM_ERROR_EPROCLIM }, */
271 { SOCEUSERS, SYSTEM_ERROR_EUSERS },
272 { SOCEDQUOT, SYSTEM_ERROR_EDQUOT },
273 { SOCESTALE, SYSTEM_ERROR_ESTALE },
274 { SOCEREMOTE, SYSTEM_ERROR_EREMOTE },
275 /* { SOCEBADRPC, SYSTEM_ERROR_EBADRPC }, */
276 /* { SOCERPCMISMATCH, SYSTEM_ERROR_ERPCMISMATCH }, */
277 /* { SOCEPROGUNAVAIL, SYSTEM_ERROR_EPROGUNAVAIL }, */
278 /* { SOCEPROGMISMATCH, SYSTEM_ERROR_EPROGMISMATCH }, */
279 /* { SOCEPROCUNAVAIL, SYSTEM_ERROR_EPROCUNAVAIL }, */
280 { SOCENOLCK, SYSTEM_ERROR_ENOLCK },
281 { SOCENOSYS, SYSTEM_ERROR_ENOSYS },
282 /* { SOCEFTYPE, SYSTEM_ERROR_EFTYPE }, */
283 /* { SOCEAUTH, SYSTEM_ERROR_EAUTH }, */
284 /* { SOCENEEDAUTH, SYSTEM_ERROR_ENEEDAUTH }, */
285 /* { SOCEOS2ERR, SYSTEM_ERROR_EOS2ERR }, */
288 static ajla_error_t error_from_os2_socket(void)
290 size_t r;
291 int rc;
292 if (unlikely(!tcpip_loaded)) {
293 return error_ajla(EC_SYSCALL, AJLA_ERROR_NOT_SUPPORTED);
295 rc = proc_sock_errno();
296 binary_search(size_t, n_array_elements(socket_error_to_system_error), r, socket_error_to_system_error[r].errn == rc, socket_error_to_system_error[r].errn < rc, return error_ajla_aux(EC_SYSCALL, AJLA_ERROR_OS2_SOCKET, rc));
297 return error_ajla_aux(EC_SYSCALL, AJLA_ERROR_SYSTEM, socket_error_to_system_error[r].sys_error);
300 uint32_t os_get_last_error(void)
302 return 0;
305 uint32_t os_get_last_socket_error(void)
307 if (unlikely(!tcpip_loaded)) {
308 return 0;
310 return proc_sock_errno();
314 void os_block_signals(sig_state_t attr_unused *set)
316 ULONG n;
317 APIRET rc = DosEnterMustComplete(&n);
318 if (unlikely(rc != 0))
319 fatal("DosEnterMustComplete failed: %lu", rc);
322 void os_unblock_signals(const sig_state_t attr_unused *set)
324 ULONG n;
325 APIRET rc = DosExitMustComplete(&n);
326 if (unlikely(rc != 0))
327 fatal("DosExitMustComplete failed: %lu", rc);
330 void attr_cold os_stop(void)
332 warning("stop not supported on OS/2");
335 void os_background(void)
339 bool os_foreground(void)
341 return true;
345 static void os2_enter_critical_section(void)
347 APIRET rc = DosEnterCritSec();
348 if (unlikely(rc != 0))
349 internal(file_line, "DosEnterCritSec returned an error: %lu", rc);
352 static void os2_exit_critical_section(void)
354 APIRET rc = DosExitCritSec();
355 if (unlikely(rc != 0))
356 internal(file_line, "DosExitCritSec returned an error: %lu", rc);
359 static void os2_close_handle(HFILE hfile)
361 APIRET rc = DosClose(hfile);
362 if (unlikely(rc != 0))
363 internal(file_line, "DosClose(%lu) returned an error: %lu", hfile, rc);
366 static void os2_close_socket(int sock)
368 proc_soclose(sock);
371 static void *os2_alloc_buffer(size_t size)
373 APIRET rc;
374 void *addr;
375 again:
376 rc = DosAllocMem(&addr, size, PAG_READ | PAG_WRITE | PAG_COMMIT);
377 if (unlikely(rc != 0)) {
378 if (rc == ERROR_INTERRUPT)
379 goto again;
380 return NULL;
382 return addr;
385 static void os2_free_buffer(void *addr)
387 APIRET rc;
388 again:
389 rc = DosFreeMem(addr);
390 if (unlikely(rc)) {
391 if (rc == ERROR_INTERRUPT)
392 goto again;
393 internal(file_line, "os2_free_buffer: DosFreeMem(%p) returned error: %lu", addr, rc);
397 static bool os2_increase_open_files(void)
399 APIRET rc;
400 rc = DosSetMaxFH(OS2_MAX_HANDLE);
401 return !rc;
404 static void os2_set_inherit(HFILE hfile, bool inherit)
406 APIRET rc;
407 ULONG state;
409 rc = DosQueryFHState(hfile, &state);
410 if (unlikely(rc != 0))
411 internal(file_line, "DosQueryFHState returned an error: %lu", rc);
413 state &= 0x8 | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_WRITE_THROUGH;
414 if (!inherit)
415 state |= OPEN_FLAGS_NOINHERIT;
417 rc = DosSetFHState(hfile, state);
418 if (unlikely(rc != 0))
419 internal(file_line, "DosSetFHState (%lu) returned an error: %lu", state, rc);
422 static bool os2_handle_is_valid(HFILE hfile)
424 APIRET rc;
425 ULONG state;
426 rc = DosQueryFHState(hfile, &state);
427 return !rc;
430 static bool os2_handle_dup1(HFILE hfile, HFILE *n, ajla_error_t *err)
432 APIRET rc;
433 bool increased = false;
434 retry:
435 *n = 0xffffffffUL;
436 rc = DosDupHandle(hfile, n);
437 if (unlikely(rc != 0)) {
438 ajla_error_t e;
439 if (!increased && rc == ERROR_TOO_MANY_OPEN_FILES) {
440 if (os2_increase_open_files()) {
441 increased = true;
442 goto retry;
445 e = error_from_os2(EC_SYSCALL, rc);
446 fatal_mayfail(e, err, "can't duplicate handle: %s", error_decode(e));
447 return false;
449 return true;
452 static bool os2_handle_dup2(HFILE hfile, HFILE n, ajla_error_t *err)
454 ULONG nn;
455 APIRET rc;
456 bool increased = false;
457 retry:
458 nn = n;
459 rc = DosDupHandle(hfile, &nn);
460 if (unlikely(rc != 0)) {
461 ajla_error_t e;
462 if (!increased && (rc == ERROR_TOO_MANY_OPEN_FILES || rc == ERROR_INVALID_TARGET_HANDLE)) {
463 if (os2_increase_open_files()) {
464 increased = true;
465 goto retry;
468 e = error_from_os2(EC_SYSCALL, rc);
469 fatal_mayfail(e, err, "can't duplicate handle: %s", error_decode(e));
470 return false;
472 return true;
475 static bool os2_handle_placeholder(HFILE h, ajla_error_t *err)
477 HFILE hfile;
478 ULONG action;
479 APIRET rc;
480 bool increased = false;
481 retry:
482 rc = DosOpen("con", &hfile, &action, 0, 0, OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT, NULL);
483 if (unlikely(rc != 0)) {
484 ajla_error_t e;
485 if (!increased && rc == ERROR_TOO_MANY_OPEN_FILES) {
486 if (os2_increase_open_files()) {
487 increased = true;
488 goto retry;
491 e = error_from_os2(EC_SYSCALL, rc);
492 fatal_mayfail(e, err, "can't open device 'con': %s", error_decode(e));
493 return false;
495 if (hfile == h)
496 return true;
497 if (unlikely(!os2_handle_dup2(hfile, h, err))) {
498 os2_close_handle(hfile);
499 return false;
501 os2_close_handle(hfile);
502 return true;
505 static handle_t os2_hfile_to_handle(HFILE hfile, int flags, char disk, ajla_error_t *err)
507 ULONG htype, hattr;
508 APIRET rc;
509 handle_t h;
511 rc = DosQueryHType(hfile, &htype, &hattr);
512 if (unlikely(rc != 0))
513 internal(file_line, "DosQueryHType returned an error: %lu", rc);
515 h = mem_calloc_mayfail(handle_t, sizeof(struct os2_handle), err);
516 if (unlikely(!h)) {
517 os2_close_handle(hfile);
518 return NULL;
521 h->h = hfile;
522 h->t = htype & 0xff;
523 h->disk = disk;
524 if (h->t == HANDTYPE_FILE)
525 flags &= ~O_NONBLOCK;
526 h->flags = flags;
527 list_init(&h->rd.wait_list);
528 list_init(&h->wr.wait_list);
529 h->rd.h = h;
530 h->ms.h = h;
531 h->wr.h = h;
533 obj_registry_insert(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
535 return h;
538 static handle_t os2_socket_to_handle(int sock, ajla_error_t *err)
540 handle_t h;
541 int one = 1;
543 if (unlikely(proc_ioctl(sock, FIONBIO, &one, sizeof one) == -1)) {
544 fatal_mayfail(error_from_os2_socket(), err, "could not set socket non-blocking");
545 os2_close_socket(sock);
546 return NULL;
549 h = mem_calloc_mayfail(handle_t, sizeof(struct os2_handle), err);
550 if (unlikely(!h)) {
551 os2_close_socket(sock);
552 return NULL;
555 h->h = sock;
556 h->t = HANDTYPE_SOCKET;
557 h->flags = O_RDWR | O_NONBLOCK;
558 list_init(&h->rd.wait_list);
559 list_init(&h->wr.wait_list);
560 h->rd.h = h;
561 h->wr.h = h;
563 obj_registry_insert(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
565 return h;
568 uintptr_t os_handle_to_number(handle_t h)
570 return h->h;
573 handle_t os_number_to_handle(uintptr_t n, bool sckt, ajla_error_t *err)
575 if (!sckt) {
576 return os2_hfile_to_handle(n, O_RDWR, 0, err);
577 } else {
578 if (unlikely(!tcpip_loaded)) {
579 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not installed");
580 return NULL;
582 return os2_socket_to_handle(n, err);
586 static void os2_clean_up_handles(void);
588 handle_t os_open(dir_handle_t dir, const char *path, int flags, int mode, ajla_error_t *err)
590 char *joined;
591 ULONG open_flag, attrs, action;
592 HFILE hfile;
593 APIRET rc;
594 bool increased;
595 handle_t h;
597 os2_clean_up_handles();
599 joined = os_join_paths(dir, path, false, err);
600 if (unlikely(!joined))
601 return NULL;
603 open_flag = 0;
604 if (flags & O_CREAT) {
605 open_flag |= OPEN_ACTION_CREATE_IF_NEW;
606 } else {
607 open_flag |= OPEN_ACTION_FAIL_IF_NEW;
609 if (flags & O_EXCL) {
610 open_flag |= OPEN_ACTION_FAIL_IF_EXISTS;
611 } else if (flags & O_TRUNC) {
612 open_flag |= OPEN_ACTION_REPLACE_IF_EXISTS;
613 } else {
614 open_flag |= OPEN_ACTION_OPEN_IF_EXISTS;
617 attrs = 0;
618 if (!(mode & 0222))
619 attrs |= FILE_READONLY;
621 increased = false;
622 retry:
623 if (likely(proc_DosOpenL != NULL)) {
624 /* * DosOpenL crashes if the address is in high memory */
625 size_t l_joined = strlen(joined) + 1;
626 unsigned char *fn;
627 if (l_joined >= 512) {
628 fn = os2_alloc_buffer(l_joined);
629 if (unlikely(!fn)) {
630 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for file name buffer (%"PRIuMAX" bytes)", (uintmax_t)l_joined);
631 mem_free(joined);
632 return NULL;
634 } else {
635 fn = alloca(l_joined);
637 memcpy(fn, joined, l_joined);
638 rc = proc_DosOpenL(fn, &hfile, &action, 0, attrs, open_flag, (flags & 0x3) | OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT, NULL);
639 if (l_joined >= 512) {
640 os2_free_buffer(fn);
642 } else {
643 rc = DosOpen(joined, &hfile, &action, 0, attrs, open_flag, (flags & 0x3) | OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT, NULL);
646 if (unlikely(rc != 0)) {
647 ajla_error_t e;
648 if (!increased && rc == ERROR_TOO_MANY_OPEN_FILES) {
649 if (os2_increase_open_files()) {
650 increased = true;
651 goto retry;
654 e = error_from_os2(EC_SYSCALL, rc);
655 fatal_mayfail(e, err, "can't open file '%s': %s", joined, error_decode(e));
656 mem_free(joined);
657 return NULL;
660 h = os2_hfile_to_handle(hfile, flags, joined[0], err);
661 mem_free(joined);
662 return h;
665 bool os_pipe(handle_t result[2], int nonblock_flags, ajla_error_t *err)
667 HFILE h1, h2;
668 APIRET rc;
669 bool increased;
671 os2_clean_up_handles();
673 increased = false;
674 retry:
675 os2_enter_critical_section();
677 rc = DosCreatePipe(&h1, &h2, OS2_PIPE_SIZE);
678 if (unlikely(rc != 0)) {
679 ajla_error_t e;
680 os2_exit_critical_section();
681 if (!increased && rc == ERROR_TOO_MANY_OPEN_FILES) {
682 if (os2_increase_open_files()) {
683 increased = true;
684 goto retry;
687 e = error_from_os2(EC_SYSCALL, rc);
688 fatal_mayfail(e, err, "can't create pipe: %s", error_decode(e));
689 return false;
692 os2_set_inherit(h1, false);
693 os2_set_inherit(h2, false);
695 os2_exit_critical_section();
697 result[0] = os2_hfile_to_handle(h1, O_RDONLY | (nonblock_flags & 1 ? O_NONBLOCK : 0), 0, err);
698 if (unlikely(!result[0])) {
699 os2_close_handle(h2);
700 return false;
702 result[1] = os2_hfile_to_handle(h2, O_WRONLY | (nonblock_flags & 2 ? O_NONBLOCK : 0), 0, err);
703 if (unlikely(!result[1])) {
704 os_close(result[0]);
705 return false;
708 return true;
712 static void os2_terminate_io_thread(struct os2_io_thread *thr);
714 static void os_free_handle(handle_t h, bool should_close)
716 ajla_assert_lo(list_is_empty(&h->rd.wait_list), (file_line, "os_free_handle: freeing handle when there are processes waiting for read"));
717 ajla_assert_lo(list_is_empty(&h->wr.wait_list), (file_line, "os_free_handle: freeing handle when there are processes waiting for write"));
718 obj_registry_remove(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
719 if (h->rd.thread) {
720 os2_terminate_io_thread(&h->rd);
722 if (h->ms.thread) {
723 os2_terminate_io_thread(&h->ms);
725 if (h->wr.thread) {
726 address_lock(h, DEPTH_THUNK);
727 if (h->wr.buffer_len != 0 && !h->wr.err) {
728 h->wr.eof = true;
729 h->wr.should_close = should_close;
730 mutex_lock(&deferred_mutex);
731 list_add(&deferred_write_list, &h->deferred_entry);
732 mutex_unlock(&deferred_mutex);
733 address_unlock(h, DEPTH_THUNK);
734 return;
736 address_unlock(h, DEPTH_THUNK);
737 os2_terminate_io_thread(&h->wr);
739 if (likely(should_close)) {
740 if (h->t == HANDTYPE_SOCKET) {
741 mutex_lock(&socket_list_mutex);
742 if (h->rd.socket_entry.next != NULL)
743 list_del(&h->rd.socket_entry);
744 if (h->wr.socket_entry.next != NULL)
745 list_del(&h->wr.socket_entry);
746 mutex_unlock(&socket_list_mutex);
747 os2_close_socket(h->h);
748 } else {
749 os2_close_handle(h->h);
752 mem_free(h);
755 void os_close(handle_t h)
757 os_free_handle(h, true);
760 unsigned os_n_std_handles(void)
762 return n_std_handles;
765 handle_t os_get_std_handle(unsigned p)
767 return os2_std_handles[p];
770 static void os2_clean_up_handles(void)
772 if (!list_is_empty(&deferred_closed_list)) {
773 mutex_lock(&deferred_mutex);
774 while (!list_is_empty(&deferred_closed_list)) {
775 handle_t h = get_struct(deferred_closed_list.prev, struct os2_handle, deferred_entry);
776 list_del(&h->deferred_entry);
777 obj_registry_insert(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
778 os_free_handle(h, false);
780 mutex_unlock(&deferred_mutex);
785 static APIRET os2_read_keyboard_packet(struct os2_io_thread *rd)
787 APIRET rc;
788 USHORT cp;
789 A_DECL(KBDKEYINFO, kbki);
790 sig_state_t s;
791 again:
793 * If we kill the thread while it's inside KbdCharIn,
794 * the keyboard deadlocks. So, we must use polling :-(
796 memset(kbki, 0, sizeof(KBDKEYINFO));
797 os_block_signals(&s);
798 rc = KbdCharIn(kbki, IO_NOWAIT, 0);
799 os_unblock_signals(&s);
800 if (unlikely(rc != 0))
801 return rc;
802 if (!kbki->chChar && !kbki->chScan) {
803 DosSleep(5);
804 goto again;
806 /*debug("key: %u, vkey: %u, fsstate: %x", kbki->chChar, kbki->chScan, kbki->fsState);*/
807 memset(&rd->pkt, 0, sizeof(struct console_read_packet));
808 rd->pkt.type = 1;
809 rd->pkt.u.k.key = kbki->chChar;
810 rd->pkt.u.k.vkey = kbki->chScan;
811 rd->pkt.u.k.ctrl = kbki->fsState;
812 os_block_signals(&s);
813 rc = KbdGetCp(0, &cp, 0);
814 os_unblock_signals(&s);
815 if (unlikely(rc != 0))
816 return rc;
817 rd->pkt.u.k.cp = cp;
818 return 0;
821 static APIRET os2_read_mouse_packet(struct os2_io_thread *rd)
823 APIRET rc;
824 USHORT w = MOU_WAIT;
825 A_DECL(MOUEVENTINFO, mei);
826 rc = MouReadEventQue(mei, &w, rd->mh);
827 if (unlikely(rc != 0))
828 return rc;
829 memset(&rd->pkt, 0, sizeof(struct console_read_packet));
830 rd->pkt.type = 2;
831 rd->pkt.u.m.x = mei->col;
832 rd->pkt.u.m.y = mei->row;
833 rd->pkt.u.m.prev_buttons = rd->last_buttons;
834 rd->last_buttons = 0;
835 if (mei->fs & (MOUSE_MOTION_WITH_BN1_DOWN | MOUSE_BN1_DOWN))
836 rd->last_buttons |= 1;
837 if (mei->fs & (MOUSE_MOTION_WITH_BN2_DOWN | MOUSE_BN2_DOWN))
838 rd->last_buttons |= 2;
839 if (mei->fs & (MOUSE_MOTION_WITH_BN3_DOWN | MOUSE_BN3_DOWN))
840 rd->last_buttons |= 4;
841 rd->pkt.u.m.buttons = rd->last_buttons;
842 rd->pkt.u.m.wx = rd->pkt.u.m.wy = 0;
843 rd->pkt.u.m.soft_cursor = rd->fullscreen;
844 return 0;
847 static void os2_read_thread(ULONG rd_)
849 struct os2_io_thread *rd = num_to_ptr(rd_);
850 handle_t h = rd->h;
851 sig_state_t s;
852 APIRET rc;
853 while (1) {
854 os_block_signals(&s);
855 address_lock(h, DEPTH_THUNK);
856 if (unlikely(rd->err) || unlikely(rd->eof)) {
857 address_unlock(h, DEPTH_THUNK);
858 os_unblock_signals(&s);
859 while (1)
860 DosSleep(-1);
861 } else if (rd->packet_mode) {
862 if (rd->packet_is_queued)
863 goto wait_for_space;
865 address_unlock(h, DEPTH_THUNK);
866 os_unblock_signals(&s);
868 if (rd == &h->rd)
869 rc = os2_read_keyboard_packet(rd);
870 else if (rd == &h->ms)
871 rc = os2_read_mouse_packet(rd);
872 else
873 internal(file_line, "os2_read_thread: invalid pointer %p, %p", rd, h);
875 os_block_signals(&s);
876 address_lock(h, DEPTH_THUNK);
878 if (!rc)
879 rd->packet_is_queued = true;
880 else
881 rd->err = rc;
882 call(wake_up_wait_list)(&h->rd.wait_list, address_get_mutex(h, DEPTH_THUNK), false);
883 os_unblock_signals(&s);
884 } else if (rd->buffer_len < OS2_BUFFER_SIZE) {
885 size_t ptr = (rd->buffer_pos + rd->buffer_len) % OS2_BUFFER_SIZE;
886 size_t len = rd->buffer_pos <= ptr ? OS2_BUFFER_SIZE - ptr : rd->buffer_pos - ptr;
887 ULONG rd_num;
888 APIRET rc;
889 address_unlock(h, DEPTH_THUNK);
890 os_unblock_signals(&s);
892 rc = DosRead(h->h, rd->buffer + ptr, len, &rd_num);
894 os_block_signals(&s);
895 address_lock(h, DEPTH_THUNK);
896 if (unlikely(rc != 0)) {
897 rd->err = rc;
899 if (unlikely(!rd_num)) {
900 rd->eof = true;
902 rd->buffer_len += rd_num;
903 call(wake_up_wait_list)(&h->rd.wait_list, address_get_mutex(h, DEPTH_THUNK), false);
904 os_unblock_signals(&s);
905 } else {
906 ULONG count;
907 wait_for_space:
908 rc = DosResetEventSem(rd->event, &count);
909 if (rc && unlikely(rc != ERROR_ALREADY_RESET))
910 internal(file_line, "DosResetEventSem returned an error: %lu", rc);
911 address_unlock(h, DEPTH_THUNK);
912 os_unblock_signals(&s);
913 wait_again:
914 rc = DosWaitEventSem(rd->event, SEM_INDEFINITE_WAIT);
915 if (unlikely(rc != 0)) {
916 if (rc == ERROR_INTERRUPT || rc == ERROR_TIMEOUT)
917 goto wait_again;
918 internal(file_line, "DosWaitEventSem returned an error: %lu", rc);
924 static void os2_write_thread(ULONG wr_)
926 struct os2_io_thread *wr = num_to_ptr(wr_);
927 handle_t h = wr->h;
928 sig_state_t s;
929 APIRET rc;
930 while (1) {
931 os_block_signals(&s);
932 address_lock(h, DEPTH_THUNK);
933 if (unlikely(wr->err)) {
934 if (wr->eof)
935 goto eof;
936 address_unlock(h, DEPTH_THUNK);
937 os_unblock_signals(&s);
938 while (1)
939 DosSleep(-1);
940 } else if (wr->buffer_len) {
941 APIRET rc;
942 ULONG wr_num;
943 size_t len = minimum(wr->buffer_len, OS2_BUFFER_SIZE - wr->buffer_pos);
944 address_unlock(h, DEPTH_THUNK);
945 os_unblock_signals(&s);
947 /*DosSleep(2000);*/
948 rc = DosWrite(h->h, wr->buffer + wr->buffer_pos, len, &wr_num);
950 os_block_signals(&s);
951 address_lock(h, DEPTH_THUNK);
952 if (unlikely(rc != 0)) {
953 wr->err = rc;
956 wr->buffer_pos = (wr->buffer_pos + wr_num) % OS2_BUFFER_SIZE;
957 wr->buffer_len -= wr_num;
959 call(wake_up_wait_list)(&h->wr.wait_list, address_get_mutex(h, DEPTH_THUNK), false);
960 os_unblock_signals(&s);
961 } else if (unlikely(wr->eof)) {
962 eof:
963 wr->buffer_len = 0;
964 if (wr->should_close)
965 os2_close_handle(h->h);
967 mutex_lock(&deferred_mutex);
968 list_del(&h->deferred_entry);
969 list_add(&deferred_closed_list, &h->deferred_entry);
970 mutex_unlock(&deferred_mutex);
972 address_unlock(h, DEPTH_THUNK);
973 os_unblock_signals(&s);
974 while (1)
975 DosSleep(-1);
976 } else {
977 ULONG count;
978 rc = DosResetEventSem(wr->event, &count);
979 if (rc && unlikely(rc != ERROR_ALREADY_RESET))
980 internal(file_line, "DosResetEventSem returned an error: %lu", rc);
981 address_unlock(h, DEPTH_THUNK);
982 os_unblock_signals(&s);
983 wait_again:
984 rc = DosWaitEventSem(wr->event, SEM_INDEFINITE_WAIT);
985 if (unlikely(rc != 0)) {
986 if (rc == ERROR_INTERRUPT || rc == ERROR_TIMEOUT)
987 goto wait_again;
988 internal(file_line, "DosWaitEventSem returned an error: %lu", rc);
994 static void os2_clear_thread_buffer(struct os2_io_thread *thr)
996 thr->eof = false;
997 thr->err = 0;
998 thr->buffer_pos = thr->buffer_len = 0;
999 thr->packet_is_queued = false;
1000 thr->last_buttons = 0;
1003 static bool os2_create_io_thread(handle_t h, struct os2_io_thread *thr, PFNTHREAD addr, ajla_error_t *err)
1005 APIRET rc;
1006 os2_clear_thread_buffer(thr);
1007 thr->packet_mode = h->packet_mode;
1008 if (!thr->packet_mode) {
1009 thr->buffer = os2_alloc_buffer(OS2_BUFFER_SIZE);
1010 if (unlikely(!thr->buffer)) {
1011 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for i/o buffer (%"PRIuMAX" bytes)", (uintmax_t)OS2_BUFFER_SIZE);
1012 goto ret1;
1014 } else {
1015 thr->buffer = NULL;
1017 rc = DosCreateEventSem(NULL, &thr->event, 0, FALSE);
1018 if (unlikely(rc != 0)) {
1019 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1020 fatal_mayfail(e, err, "DosCreateEventSem failed: %s", error_decode(e));
1021 goto ret2;
1023 if (thr == &h->ms) {
1024 HMOU mh;
1025 USHORT evmask;
1026 PTIB tib;
1027 PPIB pib;
1028 rc = DosGetInfoBlocks(&tib, &pib);
1029 if (unlikely(rc != 0)) {
1030 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1031 fatal("DosGetInfoBlocks returned an error: %s", error_decode(e));
1033 thr->fullscreen = pib->pib_ultype == 0;
1034 rc = MouOpen(NULL, &mh);
1035 if (unlikely(rc)) {
1036 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1037 fatal_mayfail(e, err, "MouOpen returned an error: %s", error_decode(e));
1038 goto ret3;
1040 thr->mh = mh;
1041 evmask = 0x7f;
1042 rc = MouSetEventMask(&evmask, mh);
1043 if (unlikely(rc)) {
1044 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1045 fatal_mayfail(e, err, "MouSetEventMask returned an error: %s", error_decode(e));
1046 goto ret4;
1049 mutex_lock(&thread_spawn_mutex);
1050 rc = DosCreateThread(&thr->thread, addr, ptr_to_num(thr), 0x2, OS2_IO_THREAD_STACK_SIZE);
1051 mutex_unlock(&thread_spawn_mutex);
1052 if (unlikely(rc != 0)) {
1053 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1054 fatal_mayfail(e, err, "can't spawn i/o thread: %s", error_decode(e));
1055 goto ret4;
1057 return true;
1059 ret4:
1060 if (thr->mh) {
1061 MouClose(thr->mh);
1062 thr->mh = 0;
1064 ret3:
1065 rc = DosCloseEventSem(thr->event);
1066 if (unlikely(rc != 0))
1067 internal(file_line, "DosCloseEventSem returned an error: %lu", rc);
1068 ret2:
1069 if (thr->buffer) {
1070 os2_free_buffer(thr->buffer);
1071 thr->buffer = NULL;
1073 ret1:
1074 return false;
1077 static void os2_terminate_thread(TID t, bool accept_invalid)
1079 APIRET rc;
1080 mutex_lock(&thread_spawn_mutex);
1081 os2_enter_critical_section(); /* avoid hang in DosRead from pipe */
1082 rc = DosKillThread(t);
1083 os2_exit_critical_section();
1084 if (unlikely(rc != 0)) {
1085 if (accept_invalid && unlikely(rc == ERROR_INVALID_THREADID))
1086 goto ok;
1087 internal(file_line, "DosKillThread (%u) returned an error: %lu", (unsigned)t, rc);
1089 rc = DosWaitThread(&t, DCWW_WAIT);
1090 if (likely(rc != 0) && unlikely(rc != ERROR_INVALID_THREADID))
1091 internal(file_line, "DosWaitThread returned an error: %lu", rc);
1093 mutex_unlock(&thread_spawn_mutex);
1096 static void os2_terminate_io_thread(struct os2_io_thread *thr)
1098 APIRET rc;
1100 os2_terminate_thread(thr->thread, false);
1102 thr->thread = 0;
1104 if (thr->buffer) {
1105 os2_free_buffer(thr->buffer);
1106 thr->buffer = NULL;
1109 rc = DosCloseEventSem(thr->event);
1110 if (unlikely(rc != 0))
1111 internal(file_line, "DosCloseEventSem returned an error: %lu", rc);
1113 if (thr->mh != 0) {
1114 MouClose(thr->mh);
1115 thr->mh = 0;
1119 static bool os2_create_read_thread(handle_t h, ajla_error_t *err)
1121 if (unlikely(!h->rd.thread))
1122 return os2_create_io_thread(h, &h->rd, os2_read_thread, err);
1123 return true;
1126 static bool os2_create_mouse_thread(handle_t h, ajla_error_t *err)
1128 if (unlikely(!h->ms.thread)) {
1129 return os2_create_io_thread(h, &h->ms, os2_read_thread, err);
1131 return true;
1134 static bool os2_create_write_thread(handle_t h, ajla_error_t *err)
1136 if (unlikely(!h->wr.thread))
1137 return os2_create_io_thread(h, &h->wr, os2_write_thread, err);
1138 return true;
1141 static void os2_terminate_read_threads(handle_t h)
1143 if (h->rd.thread)
1144 os2_terminate_io_thread(&h->rd);
1145 if (h->ms.thread)
1146 os2_terminate_io_thread(&h->ms);
1147 os2_clear_thread_buffer(&h->rd);
1148 os2_clear_thread_buffer(&h->ms);
1149 call(wake_up_wait_list)(&h->rd.wait_list, address_get_mutex(h, DEPTH_THUNK), true);
1152 static ssize_t os_read_nonblock(handle_t h, char *buffer, int size, ajla_error_t *err)
1154 ssize_t this_len;
1155 again:
1156 address_lock(h, DEPTH_THUNK);
1157 if (unlikely(h->packet_mode)) {
1158 h->packet_mode = false;
1159 os2_terminate_read_threads(h);
1160 goto again;
1162 if (unlikely(!os2_create_read_thread(h, err))) {
1163 address_unlock(h, DEPTH_THUNK);
1164 return OS_RW_ERROR;
1166 if (h->rd.buffer_len) {
1167 bool was_full = h->rd.buffer_len == OS2_BUFFER_SIZE;
1168 this_len = minimum(h->rd.buffer_len, OS2_BUFFER_SIZE - h->rd.buffer_pos);
1169 this_len = minimum(this_len, size);
1170 memcpy(buffer, h->rd.buffer + h->rd.buffer_pos, this_len);
1171 h->rd.buffer_pos = (h->rd.buffer_pos + this_len) % OS2_BUFFER_SIZE;
1172 h->rd.buffer_len -= this_len;
1173 if (was_full) {
1174 APIRET rc = DosPostEventSem(h->rd.event);
1175 if (unlikely(rc != 0) && unlikely(rc != ERROR_ALREADY_POSTED) && rc != ERROR_TOO_MANY_POSTS)
1176 internal(file_line, "DosPostEventSem returned an error: %lu", rc);
1178 } else {
1179 if (unlikely(h->rd.err != 0)) {
1180 ajla_error_t e = error_from_os2(EC_SYSCALL, h->rd.err);
1181 fatal_mayfail(e, err, "can't read handle: %s", error_decode(e));
1182 this_len = OS_RW_ERROR;
1183 goto unlock_ret;
1185 if (unlikely(h->rd.eof)) {
1186 this_len = 0;
1187 goto unlock_ret;
1189 this_len = OS_RW_WOULDBLOCK;
1191 unlock_ret:
1192 address_unlock(h, DEPTH_THUNK);
1193 return this_len;
1196 static ssize_t os_write_nonblock(handle_t h, const char *buffer, int size, ajla_error_t *err)
1198 size_t ptr;
1199 ssize_t this_len;
1200 address_lock(h, DEPTH_THUNK);
1201 if (unlikely(!os2_create_write_thread(h, err))) {
1202 address_unlock(h, DEPTH_THUNK);
1203 return OS_RW_ERROR;
1205 if (unlikely(h->wr.err)) {
1206 ajla_error_t e = error_from_os2(EC_SYSCALL, h->wr.err);
1207 fatal_mayfail(e, err, "can't write handle: %s", error_decode(e));
1208 this_len = OS_RW_ERROR;
1209 goto unlock_ret;
1211 if (h->wr.buffer_len < OS2_BUFFER_SIZE) {
1212 bool was_empty = !h->wr.buffer_len;
1213 ptr = (h->wr.buffer_pos + h->wr.buffer_len) % OS2_BUFFER_SIZE;
1214 this_len = h->wr.buffer_pos <= ptr ? OS2_BUFFER_SIZE - ptr : h->wr.buffer_pos - ptr;
1215 this_len = minimum(this_len, size);
1216 memcpy(h->wr.buffer + ptr, buffer, this_len);
1217 h->wr.buffer_len += this_len;
1218 if (was_empty) {
1219 APIRET rc = DosPostEventSem(h->wr.event);
1220 if (unlikely(rc != 0) && unlikely(rc != ERROR_ALREADY_POSTED) && rc != ERROR_TOO_MANY_POSTS)
1221 internal(file_line, "DosPostEventSem returned an error: %lu", rc);
1223 } else {
1224 this_len = OS_RW_WOULDBLOCK;
1226 unlock_ret:
1227 address_unlock(h, DEPTH_THUNK);
1228 return this_len;
1231 static bool os2_setfilesize(handle_t h, os_off_t size, ajla_error_t *err)
1233 APIRET rc;
1234 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1235 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "set file size on socket is not supported");
1236 return false;
1238 if (likely(proc_DosSetFileSizeL != NULL)) {
1239 rc = proc_DosSetFileSizeL(h->h, size);
1240 } else {
1241 if (unlikely(size != (LONG)size)) {
1242 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "file size overflow");
1243 return false;
1245 rc = DosSetFileSize(h->h, size);
1247 if (unlikely(rc != 0)) {
1248 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1249 fatal_mayfail(e, err, "can't set handle position: %s", error_decode(e));
1250 return false;
1252 return true;
1255 static bool os2_setfileptr(handle_t h, os_off_t off, ULONG rel, os_off_t *result, ajla_error_t *err)
1257 APIRET rc;
1258 ULONG ul;
1259 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1260 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "seek on socket is not supported");
1261 return false;
1263 if (likely(proc_DosSetFilePtrL != NULL)) {
1264 rc = proc_DosSetFilePtrL(h->h, off, rel, result);
1265 } else {
1266 if (unlikely(off != (LONG)off)) {
1267 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "file size overflow");
1268 return false;
1270 rc = DosSetFilePtr(h->h, off, rel, &ul);
1271 *result = ul;
1273 if (unlikely(rc != 0)) {
1274 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1275 fatal_mayfail(e, err, "can't set handle position: %s", error_decode(e));
1276 return false;
1278 return true;
1281 ssize_t os_do_rw(handle_t h, char *buffer, int size, bool wr, ajla_error_t *err)
1283 ULONG result;
1284 APIRET rc;
1285 char *bb = NULL; /* avoid warning */
1286 bool bounce = h->t == HANDTYPE_DEVICE && ptr_to_num(buffer) + size >= 0x20000000UL;
1287 if (unlikely(bounce)) {
1288 bb = os2_alloc_buffer(size);
1289 if (unlikely(!buffer)) {
1290 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for bounce buffer (%"PRIuMAX" bytes)", (uintmax_t)size);
1291 return OS_RW_ERROR;
1293 if (wr)
1294 memcpy(bb, buffer, size);
1296 if (!wr)
1297 rc = DosRead(h->h, bounce ? bb : buffer, size, &result);
1298 else
1299 rc = DosWrite(h->h, bounce ? bb : buffer, size, &result);
1300 if (unlikely(bounce)) {
1301 if (!wr && likely(!rc))
1302 memcpy(buffer, bb, result);
1303 os2_free_buffer(bb);
1305 if (unlikely(rc != 0)) {
1306 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1307 fatal_mayfail(e, err, "can't %s handle: %s", !wr ? "read from" : "write to", error_decode(e));
1308 return OS_RW_ERROR;
1310 return result;
1313 static ssize_t os_read_socket(handle_t h, char *buffer, int size, ajla_error_t *err)
1315 int r;
1316 again:
1317 r = proc_recv(h->h, buffer, size, 0);
1318 if (unlikely(r == -1)) {
1319 int er = proc_sock_errno();
1320 if (er == SOCEINTR)
1321 goto again;
1322 if (er == SOCEAGAIN)
1323 return OS_RW_WOULDBLOCK;
1324 fatal_mayfail(error_from_os2_socket(), err, "error reading socket");
1325 return OS_RW_ERROR;
1327 return r;
1330 static ssize_t os_write_socket(handle_t h, const char *buffer, int size, ajla_error_t *err)
1332 int r;
1333 again:
1334 r = proc_send(h->h, buffer, size, 0);
1335 if (unlikely(r == -1)) {
1336 int er = proc_sock_errno();
1337 if (er == SOCEINTR)
1338 goto again;
1339 if (er == SOCEAGAIN)
1340 return OS_RW_WOULDBLOCK;
1341 fatal_mayfail(error_from_os2_socket(), err, "error writing socket");
1342 return OS_RW_ERROR;
1344 return r;
1347 ssize_t os_read(handle_t h, char *buffer, int size, ajla_error_t *err)
1349 ssize_t res;
1350 if (unlikely((h->flags & 3) == O_WRONLY)) {
1351 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to read from write-only handle");
1352 return false;
1354 if (h->t == HANDTYPE_SOCKET)
1355 return os_read_socket(h, buffer, size, err);
1356 if (h->flags & O_NONBLOCK)
1357 return os_read_nonblock(h, buffer, size, err);
1358 if (likely(os_threads_initialized))
1359 address_lock(h, DEPTH_THUNK);
1360 res = os_do_rw(h, buffer, size, false, err);
1361 if (likely(os_threads_initialized))
1362 address_unlock(h, DEPTH_THUNK);
1363 return res;
1366 ssize_t os_write(handle_t h, const char *buffer, int size, ajla_error_t *err)
1368 ssize_t res;
1369 if (unlikely((h->flags & 3) == O_RDONLY)) {
1370 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to write to read-only handle");
1371 return false;
1373 if (h->t == HANDTYPE_SOCKET)
1374 return os_write_socket(h, buffer, size, err);
1375 if (h->flags & O_NONBLOCK && h->t != HANDTYPE_DEVICE)
1376 return os_write_nonblock(h, buffer, size, err);
1377 if (likely(os_threads_initialized) && h->flags & O_APPEND && h->t == HANDTYPE_FILE)
1378 address_lock(h, DEPTH_THUNK);
1379 if (h->flags & O_APPEND && h->t == HANDTYPE_FILE) {
1380 os_off_t sink;
1381 if (unlikely(!os2_setfileptr(h, 0, FILE_END, &sink, err))) {
1382 res = OS_RW_ERROR;
1383 goto unlock_ret;
1386 res = os_do_rw(h, cast_ptr(char *, buffer), size, true, err);
1387 unlock_ret:
1388 if (likely(os_threads_initialized) && h->flags & O_APPEND && h->t == HANDTYPE_FILE)
1389 address_unlock(h, DEPTH_THUNK);
1390 return res;
1393 ssize_t os_pread(handle_t h, char *buffer, int size, os_off_t off, ajla_error_t *err)
1395 ssize_t res;
1396 os_off_t sink;
1397 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1398 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "seek operation on socket");
1399 return OS_RW_ERROR;
1401 if (likely(os_threads_initialized))
1402 address_lock(h, DEPTH_THUNK);
1403 if (unlikely(!os2_setfileptr(h, off, FILE_BEGIN, &sink, err))) {
1404 res = OS_RW_ERROR;
1405 goto unlock_ret;
1407 res = os_do_rw(h, buffer, size, false, err);
1408 unlock_ret:
1409 if (likely(os_threads_initialized))
1410 address_unlock(h, DEPTH_THUNK);
1411 return res;
1414 ssize_t os_pwrite(handle_t h, const char *buffer, int size, os_off_t off, ajla_error_t *err)
1416 ssize_t res;
1417 os_off_t sink;
1418 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1419 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "seek operation on socket");
1420 return OS_RW_ERROR;
1422 if (likely(os_threads_initialized))
1423 address_lock(h, DEPTH_THUNK);
1424 if (unlikely(!os2_setfileptr(h, off, FILE_BEGIN, &sink, err))) {
1425 res = OS_RW_ERROR;
1426 goto unlock_ret;
1428 res = os_do_rw(h, cast_ptr(char *, buffer), size, true, err);
1429 unlock_ret:
1430 if (likely(os_threads_initialized))
1431 address_unlock(h, DEPTH_THUNK);
1432 return res;
1435 bool os_lseek(handle_t h, unsigned mode, os_off_t off, os_off_t *result, ajla_error_t *err)
1437 bool ret;
1438 ULONG rel;
1439 os_off_t len;
1441 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1442 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "seek operation on socket");
1443 return false;
1446 if (likely(os_threads_initialized))
1447 address_lock(h, DEPTH_THUNK);
1449 switch (mode) {
1450 case 0: rel = FILE_BEGIN; break;
1451 case 1: rel = FILE_CURRENT; break;
1452 case 2: rel = FILE_END; break;
1453 case 3: ret = os2_setfileptr(h, 0, FILE_END, &len, err);
1454 if (unlikely(!ret))
1455 goto ret_ret;
1456 if (unlikely(off > len))
1457 off = len;
1458 *result = off;
1459 ret = true;
1460 goto ret_ret;
1461 case 4: rel = FILE_END; off = 0; break;
1462 default:internal(file_line, "os_lseek: unsupported mode %u", mode);
1463 rel = (ULONG)-1; break;
1466 ret = os2_setfileptr(h, off, rel, result, err);
1468 ret_ret:
1469 if (likely(os_threads_initialized))
1470 address_unlock(h, DEPTH_THUNK);
1472 return ret;
1475 bool os_ftruncate(handle_t h, os_off_t size, ajla_error_t *err)
1477 bool ret;
1478 os_off_t current_size;
1480 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1481 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "ftruncate operation on socket");
1482 return false;
1485 if (likely(os_threads_initialized))
1486 address_lock(h, DEPTH_THUNK);
1488 ret = os2_setfileptr(h, 0, FILE_END, &current_size, err);
1490 if (size < current_size) {
1491 ret = os2_setfilesize(h, size, err);
1492 } else if (size > current_size) {
1493 os_off_t sink;
1494 ret = os2_setfileptr(h, size - 1, FILE_BEGIN, &sink, err);
1495 if (likely(ret)) {
1496 ULONG written;
1497 APIRET rc;
1498 rc = DosWrite(h->h, "", 1, &written);
1499 if (unlikely(rc != 0)) {
1500 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1501 fatal_mayfail(e, err, "extern file: %s", error_decode(e));
1502 ret = false;
1505 } else {
1506 ret = true;
1509 if (likely(os_threads_initialized))
1510 address_unlock(h, DEPTH_THUNK);
1512 return ret;
1515 bool os_fallocate(handle_t h, os_off_t position, os_off_t size, ajla_error_t *err)
1517 bool ret;
1518 os_off_t current_size;
1520 if (unlikely(h->t == HANDTYPE_SOCKET)) {
1521 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "fallocate operation on socket");
1522 return false;
1525 if (likely(os_threads_initialized))
1526 address_lock(h, DEPTH_THUNK);
1528 ret = os2_setfileptr(h, 0, FILE_END, &current_size, err);
1529 if (unlikely(!ret))
1530 goto unlock_ret;
1532 if (position <= current_size && position + size > current_size) {
1533 ret = os2_setfilesize(h, position + size, err);
1534 } else {
1535 ret = true;
1538 unlock_ret:
1539 if (likely(os_threads_initialized))
1540 address_unlock(h, DEPTH_THUNK);
1542 return ret;
1545 bool os_clone_range(handle_t attr_unused src_h, os_off_t attr_unused src_pos, handle_t attr_unused dst_h, os_off_t attr_unused dst_pos, os_off_t attr_unused len, ajla_error_t *err)
1547 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "clone not supported");
1548 return false;
1551 bool os_fsync(handle_t h, unsigned mode, ajla_error_t *err)
1553 APIRET rc;
1554 ULONG version[2];
1556 if (handle_is_valid(h) && unlikely(h->t == HANDTYPE_SOCKET)) {
1557 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "fsync operation on socket");
1558 return false;
1561 /* There is fsync bug in OS/2 before OS/2 3.0 XR_W005 */
1562 if (!DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_MINOR, version, sizeof version)) {
1563 if (version[0] == 20 && version[1] < 40)
1564 return true;
1567 if (mode == 0 || mode == 1)
1568 rc = DosResetBuffer(h->h);
1569 else
1570 rc = DosResetBuffer(0xffffffffUL);
1571 if (unlikely(rc != 0)) {
1572 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1573 fatal_mayfail(e, err, "can't flush buffers: %s", error_decode(e));
1574 return false;
1576 return true;
1580 int os_charset(void)
1582 APIRET rc;
1583 USHORT cp;
1584 rc = VioGetCp(0, &cp, 0);
1585 if (unlikely(rc != 0))
1586 return 437;
1587 return cp;
1590 ssize_t os_read_console_packet(handle_t h, struct console_read_packet *result, ajla_error_t *err)
1592 APIRET rc;
1593 ssize_t retval;
1594 again:
1595 address_lock(h, DEPTH_THUNK);
1596 if (unlikely((h->flags & 3) == O_WRONLY) ||
1597 unlikely(h->t != HANDTYPE_DEVICE)) {
1598 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to use packet console on non-console");
1599 retval = OS_RW_ERROR;
1600 goto unlock_ret;
1602 if (unlikely(!h->packet_mode)) {
1603 h->packet_mode = true;
1604 os2_terminate_read_threads(h);
1605 goto again;
1607 if (unlikely(!os2_create_read_thread(h, err))) {
1608 retval = OS_RW_ERROR;
1609 goto unlock_ret;
1611 os2_create_mouse_thread(h, err);
1612 if (h->rd.packet_is_queued) {
1613 memcpy(result, &h->rd.pkt, sizeof(struct console_read_packet));
1614 h->rd.packet_is_queued = false;
1615 rc = DosPostEventSem(h->rd.event);
1616 if (unlikely(rc != 0) && unlikely(rc != ERROR_ALREADY_POSTED) && rc != ERROR_TOO_MANY_POSTS)
1617 internal(file_line, "DosPostEventSem returned an error: %lu", rc);
1618 retval = 1;
1619 goto unlock_ret;
1621 if (h->ms.packet_is_queued) {
1622 memcpy(result, &h->ms.pkt, sizeof(struct console_read_packet));
1623 h->ms.packet_is_queued = false;
1624 rc = DosPostEventSem(h->ms.event);
1625 if (unlikely(rc != 0) && unlikely(rc != ERROR_ALREADY_POSTED) && rc != ERROR_TOO_MANY_POSTS)
1626 internal(file_line, "DosPostEventSem returned an error: %lu", rc);
1627 retval = 1;
1628 goto unlock_ret;
1630 if (unlikely(h->rd.err != 0)) {
1631 ajla_error_t e = error_from_os2(EC_SYSCALL, h->rd.err);
1632 fatal_mayfail(e, err, "can't read keyboard packet: %s", error_decode(e));
1633 retval = OS_RW_ERROR;
1634 goto unlock_ret;
1636 if (unlikely(h->ms.err != 0)) {
1637 ajla_error_t e = error_from_os2(EC_SYSCALL, h->ms.err);
1638 fatal_mayfail(e, err, "can't read mouse packet: %s", error_decode(e));
1639 retval = OS_RW_ERROR;
1640 goto unlock_ret;
1642 retval = OS_RW_WOULDBLOCK;
1643 unlock_ret:
1644 address_unlock(h, DEPTH_THUNK);
1645 return retval;
1648 bool os_write_console_packet(handle_t h, struct console_write_packet *packet, ajla_error_t *err)
1650 PCH pch;
1651 APIRET rc;
1652 if (unlikely((h->flags & 3) == O_RDONLY) ||
1653 unlikely(h->t != HANDTYPE_DEVICE)) {
1654 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to use packet console on non-console");
1655 goto err0;
1657 pch = os2_alloc_buffer(OS2_PACKET_BUFFER_SIZE);
1658 if (unlikely(!pch)) {
1659 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for write console buffer");
1660 goto err0;
1662 next:
1663 switch (packet->type) {
1664 case 1: {
1665 break;
1667 case 2: {
1668 int x, y;
1669 unsigned n_chars;
1670 int32_t *ptr;
1671 x = packet->u.c.x;
1672 y = packet->u.c.y;
1673 n_chars = packet->u.c.n_chars;
1674 ptr = packet->u.c.data;
1675 while (n_chars) {
1676 BYTE attr[3];
1677 unsigned n, i;
1678 for (n = 1; n < n_chars && n < OS2_PACKET_BUFFER_SIZE; n++) {
1679 if (ptr[n * 2 + 1] != ptr[1])
1680 break;
1682 for (i = 0; i < n; i++) {
1683 pch[i] = ptr[i * 2];
1685 rc = VioWrtCharStr(pch, n, y, x, 0);
1686 if (unlikely(rc != 0)) {
1687 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1688 fatal_mayfail(e, err, "VioWrtCharStr failed: %s", error_decode(e));
1689 goto err1;
1691 attr[0] = ptr[1];
1692 attr[1] = 0;
1693 attr[2] = 0;
1694 rc = VioWrtNAttr(attr, n, y, x, 0);
1695 if (unlikely(rc != 0)) {
1696 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1697 fatal_mayfail(e, err, "VioWrtNAttr failed: %s", error_decode(e));
1698 goto err1;
1701 x += n;
1702 ptr += n * 2;
1703 n_chars -= n;
1705 packet = cast_ptr(struct console_write_packet *, &packet->u.c.data[packet->u.c.n_chars * 2]);
1706 goto next;
1708 case 3: {
1709 rc = VioSetCurPos(packet->u.p.y, packet->u.p.x, 0);
1710 if (unlikely(rc != 0)) {
1711 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1712 fatal_mayfail(e, err, "VioSetCurPos failed: %s", error_decode(e));
1713 goto err1;
1715 packet = cast_ptr(struct console_write_packet *, &packet->u.p.end);
1716 goto next;
1718 case 4: {
1719 A_DECL(VIOCURSORINFO, vci);
1720 rc = VioGetCurType(vci, 0);
1721 if (unlikely(rc != 0)) {
1722 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1723 fatal_mayfail(e, err, "VioGetCurType failed: %s", error_decode(e));
1724 goto err1;
1726 vci->attr = packet->u.v.v ? 0 : -1;
1727 rc = VioSetCurType(vci, 0);
1728 if (unlikely(rc != 0)) {
1729 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1730 fatal_mayfail(e, err, "VioSetCurType failed: %s", error_decode(e));
1731 goto err1;
1733 packet = cast_ptr(struct console_write_packet *, &packet->u.v.end);
1734 goto next;
1736 default: {
1737 internal(file_line, "os_write_console_packet: invalid type %d", (int)packet->type);
1738 break;
1741 os2_free_buffer(pch);
1742 return true;
1744 err1:
1745 os2_free_buffer(pch);
1746 err0:
1747 return false;
1751 dir_handle_t os_dir_root(ajla_error_t *err)
1753 unsigned bit;
1754 APIRET rc;
1755 ULONG current, drv;
1756 char *d = str_dup(" :\\", -1, err);
1757 if (unlikely(!d))
1758 return NULL;
1759 rc = DosQueryCurrentDisk(&current, &drv);
1760 if (unlikely(rc != 0)) {
1761 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1762 fatal_mayfail(e, err, "DosQueryCurrentDisk failed: %s", error_decode(e));
1763 mem_free(d);
1764 return NULL;
1766 if (unlikely(!drv))
1767 drv = 4;
1768 if (drv & ~3U)
1769 drv &= ~3U;
1770 bit = low_bit(drv);
1771 d[0] = 'A' + bit;
1772 return d;
1775 dir_handle_t os_dir_cwd(ajla_error_t *err)
1777 ULONG disk, logical;
1778 APIRET rc;
1779 char *ptr, *p;
1780 size_t len;
1782 char *buffer;
1783 ULONG buffer_len;
1785 if (unlikely(!array_init_mayfail(char, &ptr, &len, err)))
1786 return dir_none;
1788 rc = DosQueryCurrentDisk(&disk, &logical);
1789 if (unlikely(rc)) {
1790 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1791 fatal_mayfail(e, err, "DosQueryCurrentDisk failed: %s", error_decode(e));
1792 mem_free(ptr);
1793 return dir_none;
1796 if (unlikely(!array_add_mayfail(char, &ptr, &len, disk + 'A' - 1, NULL, err)))
1797 return dir_none;
1798 if (unlikely(!array_add_multiple_mayfail(char, &ptr, &len, ":\\", 2, NULL, err)))
1799 return dir_none;
1801 buffer_len = 1;
1802 alloc_again:
1803 buffer = mem_alloc_mayfail(char *, buffer_len, err);
1804 if (unlikely(!buffer)) {
1805 mem_free(ptr);
1806 return dir_none;
1808 rc = DosQueryCurrentDir(disk, buffer, &buffer_len);
1809 if (rc == ERROR_BUFFER_OVERFLOW) {
1810 mem_free(buffer);
1811 goto alloc_again;
1813 if (unlikely(rc)) {
1814 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1815 fatal_mayfail(e, err, "DosQueryCurrentDir failed: %s", error_decode(e));
1816 mem_free(ptr);
1817 mem_free(buffer);
1818 return dir_none;
1820 if (unlikely(!array_add_multiple_mayfail(char, &ptr, &len, buffer, strlen(buffer) + 1, NULL, err))) {
1821 mem_free(buffer);
1822 return dir_none;
1824 mem_free(buffer);
1825 array_finish(char, &ptr, &len);
1826 p = ptr;
1827 while ((p = strchr(p, '\\')))
1828 *p++ = '/';
1829 return ptr;
1832 static bool os2_dir_set(dir_handle_t dir, ajla_error_t *err)
1834 APIRET rc;
1835 if ((dir[0] & 0xdf) >= 'A' && (dir[0] & 0xdf) <= 'Z' && dir[1] == ':') {
1836 rc = DosSetDefaultDisk((dir[0] & 0xdf) - 'A' + 1);
1837 if (unlikely(rc != 0)) {
1838 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1839 fatal_mayfail(e, err, "can't set current disk '%c:': %s", dir[0] & 0xdf, error_decode(e));
1840 return false;
1842 dir += 2;
1844 rc = DosSetCurrentDir(dir);
1845 if (unlikely(rc != 0)) {
1846 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1847 fatal_mayfail(e, err, "can't set current directory '%s': %s", dir, error_decode(e));
1848 return false;
1850 return true;
1853 dir_handle_t os_dir_open(dir_handle_t dir, const char *path, int attr_unused flags, ajla_error_t *err)
1855 char *result;
1856 FILESTATUS3 fs;
1857 APIRET rc;
1858 result = os_join_paths(dir, path, true, err);
1859 if (unlikely(!result))
1860 return dir_none;
1861 rc = DosQueryPathInfo(result, FIL_STANDARD, &fs, sizeof fs);
1862 if (unlikely(rc != 0)) {
1863 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
1864 fatal_mayfail(e, err, "can't open directory '%s': %s", result, error_decode(e));
1865 mem_free(result);
1866 return dir_none;
1868 if (unlikely(!(fs.attrFile & FILE_DIRECTORY))) {
1869 ajla_error_t e = error_from_os2(EC_SYSCALL, ERROR_PATH_NOT_FOUND);
1870 fatal_mayfail(e, err, "can't open directory '%s': %s", result, error_decode(e));
1871 mem_free(result);
1872 return dir_none;
1874 return result;
1877 void os_dir_close(dir_handle_t h)
1879 mem_free(h);
1882 char *os_dir_path(dir_handle_t h, ajla_error_t *err)
1884 return str_dup(h, -1, err);
1887 #define FIND_BUFFER_SIZE 65535
1889 static void os_close_dir(HDIR hdir)
1891 APIRET rc = DosFindClose(hdir);
1892 if (unlikely(rc != 0))
1893 internal(file_line, "DosFindClose returned an error: %lu", rc);
1896 static bool process_find_buffer(char *buffer, size_t n_entries, char ***files, size_t *n_files, ajla_error_t *err)
1898 while (n_entries--) {
1899 void *err_ptr;
1900 FILEFINDBUF3 *ffb = cast_ptr(FILEFINDBUF3 *, buffer);
1901 char *name = ffb->achName;
1902 buffer += ffb->oNextEntryOffset;
1903 if (unlikely(!strcmp(name, ".")) || unlikely(!strcmp(name, "..")))
1904 continue;
1905 name = str_dup(name, -1, err);
1906 if (unlikely(!name))
1907 return false;
1908 if (unlikely(!array_add_mayfail(char *, files, n_files, name, &err_ptr, err))) {
1909 *files = err_ptr;
1910 return false;
1913 return true;
1916 bool os_dir_read(dir_handle_t h, char ***files, size_t *n_files, ajla_error_t *err)
1918 HDIR hdir = HDIR_CREATE;
1919 char find_buffer[FIND_BUFFER_SIZE];
1920 char *fn;
1921 ULONG n_entries;
1922 APIRET rc;
1924 if (unlikely(!array_init_mayfail(char *, files, n_files, err)))
1925 return false;
1927 fn = os_join_paths(h, "*", false, err);
1928 if (unlikely(!fn))
1929 goto ret_false;
1931 n_entries = FIND_BUFFER_SIZE;
1932 rc = DosFindFirst(fn, &hdir, FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY | FILE_ARCHIVED, find_buffer, FIND_BUFFER_SIZE, &n_entries, FIL_STANDARD);
1933 mem_free(fn);
1934 if (unlikely(rc != 0)) {
1935 ajla_error_t e;
1936 if (likely(rc == ERROR_NO_MORE_FILES))
1937 goto ret_array;
1938 e = error_from_os2(EC_SYSCALL, rc);
1939 fatal_mayfail(e, err, "error reading directory '%s': %s", h, error_decode(e));
1940 goto ret_false;
1942 proc_buffer:
1943 if (unlikely(!process_find_buffer(find_buffer, n_entries, files, n_files, err))) {
1944 os_close_dir(hdir);
1945 goto ret_false;
1947 n_entries = FIND_BUFFER_SIZE;
1948 rc = DosFindNext(hdir, find_buffer, FIND_BUFFER_SIZE, &n_entries);
1949 if (likely(rc != 0)) {
1950 ajla_error_t e;
1951 os_close_dir(hdir);
1952 if (likely(rc == ERROR_NO_MORE_FILES))
1953 goto ret_array;
1954 e = error_from_os2(EC_SYSCALL, rc);
1955 fatal_mayfail(e, err, "error reading directory '%s': %s", h, error_decode(e));
1956 goto ret_false;
1958 goto proc_buffer;
1960 ret_array:
1961 return true;
1963 ret_false:
1964 os_dir_free(*files, *n_files);
1965 return false;
1968 void os_dir_free(char **files, size_t n_files)
1970 size_t i;
1971 for (i = 0; i < n_files; i++)
1972 mem_free(files[i]);
1973 mem_free(files);
1977 unsigned os_dev_t_major(dev_t attr_unused dev)
1979 return 0;
1982 unsigned os_dev_t_minor(dev_t attr_unused dev)
1984 return 0;
1987 static os_time_t time_to_os_time(int year, int month, int day, int hour, int min, int sec)
1989 os_time_t x;
1991 /*debug("y=%d, m=%d, d=%d, h=%d, m=%d, s=%d", year, month, day, hour, min, sec);*/
1993 x = (os_time_t)year * 365 + day + (month - 1) * 31;
1994 if (month <= 2)
1995 year--;
1996 else
1997 x -= (month * 4 + 23) / 10;
1998 x += year / 4 - ((year / 100 + 1) * 3) / 4;
2000 x = (x - 719528) * 24 * 3600;
2002 x += hour * 3600 + min * 60 + sec;
2004 return x;
2007 static void os_time_to_time(os_time_t t, int *year, int *month, int *day, int *hour, int *min, int *sec)
2009 time_t tim = t;
2010 struct tm *tm;
2011 if (os_threads_initialized)
2012 address_lock(NULL, DEPTH_THUNK);
2013 tm = gmtime(&tim);
2014 if (unlikely(!tm)) {
2015 *year = *month = *day = *hour = *min = *sec = 0;
2016 } else {
2017 *year = tm->tm_year + 1900;
2018 *month = tm->tm_mon;
2019 *day = tm->tm_mday;
2020 *hour = tm->tm_hour;
2021 *min = tm->tm_min;
2022 *sec = tm->tm_sec;
2024 if (os_threads_initialized)
2025 address_unlock(NULL, DEPTH_THUNK);
2028 #if 0
2029 time_t _mktime(struct tm *);
2030 static os_time_t file_time_to_os_time(FDATE *fd, FTIME *ft)
2032 struct tm tm;
2033 tm.tm_year = fd->year + 80;
2034 tm.tm_mon = fd->month - 1;
2035 tm.tm_mday = fd->day;
2036 tm.tm_hour = ft->hours;
2037 tm.tm_min = ft->minutes;
2038 tm.tm_sec = ft->twosecs * 2;
2039 return _mktime(&tm);
2041 #else
2042 static os_time_t file_time_to_os_time(FDATE *fd, FTIME *ft)
2044 int year, month, day, hour, min, sec;
2046 year = fd->year + 1980;
2047 month = fd->month;
2048 day = fd->day;
2049 hour = ft->hours;
2050 min = ft->minutes;
2051 sec = ft->twosecs * 2;
2053 return time_to_os_time(year, month, day, hour, min, sec) + timezone;
2056 static void os_time_to_file_time(os_time_t t, FDATE *fd, FTIME *ft)
2058 int year, month, day, hour, min, sec;
2059 t -= timezone;
2060 os_time_to_time(t, &year, &month, &day, &hour, &min, &sec);
2061 fd->year = year - 1980;
2062 fd->month = month;
2063 fd->day = day;
2064 ft->hours = hour;
2065 ft->minutes = min;
2066 ft->twosecs = sec / 2;
2068 #endif
2070 ajla_time_t os_time_t_to_ajla_time(os_time_t sec)
2072 return (ajla_time_t)sec * 1000000;
2075 static os_time_t ajla_time_to_os_time(ajla_time_t usec)
2077 return usec / 1000000;
2080 bool os_fstat(handle_t h, os_stat_t *st, ajla_error_t *err)
2082 FILESTATUS3 info;
2083 ULONG state;
2084 APIRET rc;
2086 memset(st, 0, sizeof(os_stat_t));
2087 st->st_nlink = 1;
2089 switch (h->t) {
2090 case HANDTYPE_SOCKET:
2091 st->st_mode = S_IFSOCK | 0600;
2092 break;
2093 case HANDTYPE_DEVICE:
2094 st->st_mode = S_IFCHR;
2095 goto qfhstate;
2096 case HANDTYPE_PIPE:
2097 st->st_mode = S_IFIFO;
2098 qfhstate:
2099 rc = DosQueryFHState(h->h, &state);
2100 if (unlikely(rc != 0))
2101 internal(file_line, "DosQueryFHState returned an error: %lu", rc);
2102 if ((state & 7) == OPEN_ACCESS_READONLY)
2103 st->st_mode |= 0444;
2104 else
2105 st->st_mode |= 0666;
2106 break;
2107 default:
2108 st->st_mode = S_IFREG;
2109 rc = DosQueryFileInfo(h->h, FIL_STANDARD, &info, sizeof(info));
2110 if (unlikely(rc != 0)) {
2111 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2112 fatal_mayfail(e, err, "DosQueryFileInfo returned an error: %s", error_decode(e));
2113 return false;
2115 st->st_ctime = file_time_to_os_time(&info.fdateCreation, &info.ftimeCreation);
2116 st->st_atime = file_time_to_os_time(&info.fdateLastAccess, &info.ftimeLastAccess);
2117 st->st_mtime = file_time_to_os_time(&info.fdateLastWrite, &info.ftimeLastWrite);
2118 if (unlikely((info.attrFile & 0x01) != 0))
2119 st->st_mode |= 0444;
2120 else
2121 st->st_mode |= 0666;
2122 st->st_size = info.cbFile;
2123 st->st_blocks = round_up(st->st_size, 512) / 512;
2124 st->st_blksize = 512;
2125 break;
2128 return true;
2131 bool os_stat(dir_handle_t dir, const char *path, bool attr_unused lnk, os_stat_t *st, ajla_error_t *err)
2133 ajla_error_t sink;
2134 dir_handle_t dh;
2135 FILESTATUS3 info;
2136 APIRET rc;
2138 memset(st, 0, sizeof(os_stat_t));
2139 st->st_nlink = 1;
2141 dh = os_dir_open(dir, path, 0, &sink);
2142 if (dir_handle_is_valid(dh)) {
2143 st->st_mode = S_IFDIR | 0777;
2144 } else {
2145 st->st_mode = S_IFREG | 0666;
2146 dh = os_join_paths(dir, path, false, err);
2147 if (unlikely(!dh))
2148 return false;
2150 rc = DosQueryPathInfo(dh, FIL_STANDARD, &info, sizeof info);
2151 if (unlikely(rc != 0)) {
2152 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2153 fatal_mayfail(e, err, "can't access path '%s': %s", dh, error_decode(e));
2154 os_dir_close(dh);
2155 return false;
2157 st->st_ctime = file_time_to_os_time(&info.fdateCreation, &info.ftimeCreation);
2158 st->st_atime = file_time_to_os_time(&info.fdateLastAccess, &info.ftimeLastAccess);
2159 st->st_mtime = file_time_to_os_time(&info.fdateLastWrite, &info.ftimeLastWrite);
2160 if (unlikely((info.attrFile & 1) != 0))
2161 st->st_mode &= ~0222;
2162 st->st_size = info.cbFile;
2163 st->st_blocks = round_up(st->st_size, 512) / 512;
2164 st->st_blksize = 512;
2165 os_dir_close(dh);
2166 return true;
2169 static bool os_stat_disk(char disk, os_statvfs_t *st, ajla_error_t *err)
2171 FSALLOCATE fsal;
2172 APIRET rc;
2174 disk &= 0xdf;
2175 if (unlikely(disk < 'A') || unlikely(disk > 'Z')) {
2176 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "statvfs not supported");
2177 return false;
2179 rc = DosQueryFSInfo(disk - 'A' + 1, FSIL_ALLOC, &fsal, sizeof(FSALLOCATE));
2180 if (unlikely(rc != 0)) {
2181 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2182 fatal_mayfail(e, err, "can't get disk '%c' free space: %s", disk - 1 + 'A', error_decode(e));
2183 return false;
2185 memset(st, 0, sizeof(os_statvfs_t));
2186 st->f_bsize = st->f_frsize = fsal.cSectorUnit * fsal.cbSector;
2187 st->f_blocks = fsal.cUnit;
2188 st->f_bfree = st->f_bavail = fsal.cUnitAvail;
2189 st->f_fsid = fsal.idFileSystem;
2190 st->f_namemax = 255;
2191 return true;
2194 bool os_fstatvfs(handle_t h, os_statvfs_t *st, ajla_error_t *err)
2196 return os_stat_disk(h->disk, st, err);
2199 bool os_dstatvfs(dir_handle_t dir, os_statvfs_t *st, ajla_error_t *err)
2201 return os_stat_disk(dir[0], st, err);
2204 char *os_readlink(dir_handle_t attr_unused dir, const char attr_unused *path, ajla_error_t *err)
2206 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "readlink not supported");
2207 return NULL;
2210 bool os_dir_action(dir_handle_t dir, const char *path, int action, int attr_unused mode, ajla_time_t attr_unused dev_major, ajla_time_t attr_unused dev_minor, const char attr_unused *syml, ajla_error_t *err)
2212 APIRET rc;
2213 FILESTATUS3 fs;
2214 char *joined;
2215 bool allow_trailing_slash = action == IO_Action_Rm_Dir || action == IO_Action_Mk_Dir;
2217 joined = os_join_paths(dir, path, allow_trailing_slash, err);
2218 if (unlikely(!joined))
2219 return false;
2221 switch (action) {
2222 case IO_Action_Rm:
2223 rc = DosDelete(joined);
2224 break;
2225 case IO_Action_Rm_Dir:
2226 rc = DosDeleteDir(joined);
2227 break;
2228 case IO_Action_Mk_Dir:
2229 rc = DosCreateDir(joined, NULL);
2230 if (rc == ERROR_ACCESS_DENIED) {
2231 APIRET rc2;
2232 rc2 = DosQueryPathInfo(joined, FIL_STANDARD, &fs, sizeof fs);
2233 if (!rc2)
2234 rc = ERROR_FILE_EXISTS;
2236 break;
2237 case IO_Action_Mk_Pipe:
2238 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkpipe not supported");
2239 goto ret_false;
2240 case IO_Action_Mk_Socket:
2241 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mksocket not supported");
2242 goto ret_false;
2243 case IO_Action_Mk_CharDev:
2244 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkchardev not supported");
2245 goto ret_false;
2246 case IO_Action_Mk_BlockDev:
2247 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkblockdev not supported");
2248 goto ret_false;
2249 case IO_Action_Mk_SymLink:
2250 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mksymlink not supported");
2251 goto ret_false;
2252 case IO_Action_ChMod:
2253 case IO_Action_ChOwn:
2254 case IO_Action_LChOwn:
2255 rc = DosQueryPathInfo(joined, FIL_STANDARD, &fs, sizeof fs);
2256 break;
2257 case IO_Action_UTime:
2258 case IO_Action_LUTime: {
2259 rc = DosQueryPathInfo(joined, FIL_STANDARD, &fs, sizeof fs);
2260 if (unlikely(rc))
2261 break;
2262 os_time_to_file_time(ajla_time_to_os_time(dev_major), &fs.fdateLastWrite, &fs.ftimeLastWrite);
2263 os_time_to_file_time(ajla_time_to_os_time(dev_minor), &fs.fdateLastAccess, &fs.ftimeLastAccess);
2264 rc = DosSetPathInfo(joined, FIL_STANDARD, &fs, sizeof(fs), 0);
2265 break;
2267 default:
2268 internal(file_line, "os_dir_action: invalid action %d", action);
2270 if (unlikely(rc != 0)) {
2271 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2272 fatal_mayfail(e, err, "can't perform action %d on '%s': %s", action, joined, error_decode(e));
2273 goto ret_false;
2275 mem_free(joined);
2276 return true;
2278 ret_false:
2279 mem_free(joined);
2280 return false;
2283 bool os_dir2_action(dir_handle_t dest_dir, const char *dest_path, int action, dir_handle_t src_dir, const char *src_path, ajla_error_t *err)
2285 APIRET rc;
2286 FILESTATUS3 fs;
2287 char *dest_joined = NULL;
2288 char *src_joined = NULL;
2290 dest_joined = os_join_paths(dest_dir, dest_path, false, err);
2291 if (unlikely(!dest_joined))
2292 goto ret_false;
2293 src_joined = os_join_paths(src_dir, src_path, false, err);
2294 if (unlikely(!src_joined))
2295 goto ret_false;
2297 switch (action) {
2298 case IO_Action_Mk_Link:
2299 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mklink not supported");
2300 goto ret_false;
2301 case IO_Action_Rename:
2302 rc = DosQueryPathInfo(src_joined, FIL_STANDARD, &fs, sizeof fs);
2303 if (likely(!rc)) {
2304 DosDelete(dest_joined);
2305 rc = DosMove(src_joined, dest_joined);
2307 break;
2308 default:
2309 internal(file_line, "os_dir2_action: invalid action %d", action);
2312 if (unlikely(rc != 0)) {
2313 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2314 fatal_mayfail(e, err, "can't perform action %d on '%s' and '%s': %s", action, src_joined, dest_joined, error_decode(e));
2315 goto ret_false;
2318 mem_free(dest_joined);
2319 mem_free(src_joined);
2320 return true;
2322 ret_false:
2323 if (dest_joined)
2324 mem_free(dest_joined);
2325 if (src_joined)
2326 mem_free(src_joined);
2327 return false;
2330 bool os_drives(char **drives, size_t *drives_l, ajla_error_t *err)
2332 ULONG c, mask;
2333 APIRET rc;
2334 /* copied in os_win32.c:os_drives */
2335 again:
2336 rc = DosQueryCurrentDisk(&c, &mask);
2337 if (unlikely(rc)) {
2338 ajla_error_t e;
2339 if (rc == ERROR_INTERRUPT)
2340 goto again;
2341 e = error_from_os2(EC_SYSCALL, rc);
2342 fatal_mayfail(e, err, "can't query current disk: %s", error_decode(e));
2343 return false;
2345 return os_drives_bitmap(mask, drives, drives_l, err);
2349 static unsigned char os_path_to_exe[270];
2351 const char *os_get_path_to_exe(void)
2353 return os_path_to_exe;
2356 static void os_init_path_to_exe(void)
2358 APIRET rc;
2359 PTIB tib;
2360 PPIB pib;
2361 size_t i, j;
2362 rc = DosGetInfoBlocks(&tib, &pib);
2363 if (unlikely(rc != 0)) {
2364 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2365 fatal("DosGetInfoBlocks returned an error: %s", error_decode(e));
2367 rc = DosQueryModuleName(pib->pib_hmte, sizeof os_path_to_exe, os_path_to_exe);
2368 if (unlikely(rc != 0)) {
2369 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2370 fatal("DosQueryModuleName returned an error: %s", error_decode(e));
2372 j = 0;
2373 for (i = 0; os_path_to_exe[i]; i++)
2374 if (os_is_path_separator(os_path_to_exe[i]))
2375 j = i + 1;
2376 os_path_to_exe[j] = 0;
2380 bool os_tcgetattr(handle_t attr_unused h, os_termios_t *t, ajla_error_t *err)
2382 APIRET rc;
2383 A_DECL(KBDINFO, kbi);
2384 rc = KbdGetStatus(kbi, 0);
2385 if (unlikely(rc != 0)) {
2386 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2387 fatal_mayfail(e, err, "KbdGetStatus returned an error: %s", error_decode(e));
2388 return false;
2390 t->tc_flags = 0;
2391 if (kbi->fsMask & 2)
2392 t->tc_flags |= IO_Stty_Flag_Noecho;
2393 return true;
2396 bool os_tcsetattr(handle_t attr_unused h, const os_termios_t *t, ajla_error_t *err)
2398 APIRET rc;
2399 A_DECL(KBDINFO, kbi);
2400 rc = KbdGetStatus(kbi, 0);
2401 if (unlikely(rc != 0)) {
2402 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2403 fatal_mayfail(e, err, "KbdGetStatus returned an error: %s", error_decode(e));
2404 return false;
2406 kbi->fsMask &= ~3;
2407 if (t->tc_flags & IO_Stty_Flag_Noecho)
2408 kbi->fsMask |= 2;
2409 else
2410 kbi->fsMask |= 1;
2411 rc = KbdSetStatus(kbi, 0);
2412 if (unlikely(rc != 0)) {
2413 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2414 fatal_mayfail(e, err, "KbdSetStatus returned an error: %s", error_decode(e));
2415 return false;
2417 return true;
2420 void os_tcflags(os_termios_t *t, int flags)
2422 t->tc_flags = flags;
2425 bool os_tty_size(handle_t attr_unused h, int *nx, int *ny, int *ox, int *oy, ajla_error_t *err)
2427 A_DECL(VIOMODEINFO, vmi);
2428 APIRET rc;
2429 vmi->cb = sizeof(VIOMODEINFO);
2430 rc = VioGetMode(vmi, 0);
2431 if (unlikely(rc != 0)) {
2432 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2433 fatal_mayfail(e, err, "can't get tty size: %s", error_decode(e));
2434 return false;
2437 *nx = vmi->col;
2438 *ny = vmi->row;
2439 *ox = 0;
2440 *oy = 0;
2442 return true;
2446 const char *os_get_flavor(void)
2448 return "OS/2";
2451 void os_get_uname(os_utsname_t *un)
2453 ULONG version[2];
2455 memset(un, 0, sizeof(os_utsname_t));
2457 strcpy(un->sysname, "OS/2");
2459 if (!DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_MINOR, version, sizeof version)) {
2460 if (version[0] == 20) {
2461 version[0] = 2;
2462 if (version[1] == 10) {
2463 version[1] = 1;
2464 } else if (version[1] >= 30) {
2465 version[0] = version[1] / 10;
2466 version[1] %= 10;
2469 sprintf(un->release, "%d.%d", (int)version[0], (int)version[1]);
2472 #ifdef ARCH_NAME
2473 strcpy(un->machine, ARCH_NAME);
2474 #endif
2477 char *os_get_host_name(ajla_error_t *err)
2479 char *e;
2480 if (tcpip_loaded) {
2481 char nodename[256] = "";
2482 proc_gethostname(nodename, sizeof nodename - 1);
2483 if (nodename[0])
2484 return str_dup(nodename, -1, err);
2486 e = getenv("HOSTNAME");
2487 if (!e)
2488 e = "";
2489 return str_dup(e, -1, err);
2493 #if 0
2494 static ajla_time_t os_timeval_to_ajla_time(const struct timeval *tv)
2496 return os_time_t_to_ajla_time(tv->tv_sec) + tv->tv_usec;
2498 ajla_time_t os_time_real(void)
2500 int r;
2501 struct timeval tv;
2502 EINTR_LOOP(r, gettimeofday(&tv, NULL));
2503 if (unlikely(r == -1)) {
2504 int e = errno;
2505 fatal("gettimeofday failed: %d, %s", e, error_decode(error_from_errno(EC_SYSCALL, e)));
2507 return os_timeval_to_ajla_time(&tv);
2509 #else
2510 ajla_time_t os_time_real(void)
2512 APIRET rc;
2513 DATETIME dt;
2514 int year, month, day, hour, min, sec;
2515 os_time_t ost;
2516 rc = DosGetDateTime(&dt);
2517 if (unlikely(rc != 0)) {
2518 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2519 fatal("DosGetDateTime returned an error: %s", error_decode(e));
2521 year = dt.year;
2522 month = dt.month;
2523 day = dt.day;
2524 hour = dt.hours;
2525 min = dt.minutes;
2526 sec = dt.seconds;
2527 ost = time_to_os_time(year, month, day, hour, min, sec) + timezone;
2528 return os_time_t_to_ajla_time(ost) + dt.hundredths * 10000;
2530 #endif
2532 static mutex_t tick_mutex;
2533 static ULONG tick_last;
2534 static ULONG tick_high;
2536 ajla_time_t os_time_monotonic(void)
2538 APIRET rc;
2539 ULONG t;
2540 ajla_time_t ret;
2542 if (likely(proc_DosTmrQueryTime != NULL)) {
2543 union {
2544 QWORD qw;
2545 long long ll;
2546 } q;
2547 rc = proc_DosTmrQueryTime(&q.qw);
2548 if (unlikely(rc != 0)) {
2549 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2550 fatal("DosTmrQueryTime returned an error: %s", error_decode(e));
2552 return (ajla_time_t)(q.ll * freq_period_usec);
2555 if (likely(os_threads_initialized))
2556 mutex_lock(&tick_mutex);
2557 rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &t, sizeof t);
2558 if (unlikely(rc)) {
2559 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2560 fatal("DosQuerySysInfo(%u) returned an error: %s", QSV_MS_COUNT, error_decode(e));
2562 if (unlikely(t < tick_last))
2563 tick_high++;
2564 tick_last = t;
2565 ret = ((ajla_time_t)tick_high * (1 << 31) * 2) + t;
2566 if (likely(os_threads_initialized))
2567 mutex_unlock(&tick_mutex);
2568 return ret * 1000;
2572 void iomux_never(mutex_t **mutex_to_lock, struct list *list_entry)
2574 *mutex_to_lock = address_get_mutex(NULL, DEPTH_THUNK);
2575 list_init(list_entry);
2578 static void os2_notify(void);
2580 void iomux_register_wait(handle_t h, bool wr, mutex_t **mutex_to_lock, struct list *list_entry)
2582 struct os2_io_thread *thr;
2583 thr = !wr ? &h->rd : &h->wr;
2584 address_lock(h, DEPTH_THUNK);
2585 *mutex_to_lock = address_get_mutex(h, DEPTH_THUNK);
2586 list_add(&thr->wait_list, list_entry);
2587 if (h->t == HANDTYPE_SOCKET) {
2588 address_unlock(h, DEPTH_THUNK);
2589 mutex_lock(&socket_list_mutex);
2590 if (thr->socket_entry.next == NULL)
2591 list_add(&socket_list[(int)wr], &thr->socket_entry);
2592 mutex_unlock(&socket_list_mutex);
2593 os2_notify();
2594 return;
2596 if (!wr) {
2597 if (unlikely(h->rd.buffer_len != 0) || unlikely(h->rd.packet_is_queued) || unlikely(h->rd.err != 0) || unlikely(h->rd.eof))
2598 goto wake_up;
2599 if (unlikely(h->ms.buffer_len != 0) || unlikely(h->ms.packet_is_queued) || unlikely(h->ms.err != 0) || unlikely(h->ms.eof))
2600 goto wake_up;
2601 } else {
2602 if (unlikely(h->wr.buffer_len != OS2_BUFFER_SIZE) || unlikely(h->wr.err != 0))
2603 goto wake_up;
2605 address_unlock(h, DEPTH_THUNK);
2606 return;
2608 wake_up:
2609 call(wake_up_wait_list)(&thr->wait_list, address_get_mutex(h, DEPTH_THUNK), true);
2612 bool iomux_test_handle(handle_t h, bool wr)
2614 if (h->t == HANDTYPE_SOCKET) {
2615 int sel[1];
2616 int r;
2617 again:
2618 sel[0] = h->h;
2619 r = proc_select(sel, !wr, wr, 0, 0);
2620 if (unlikely(r == -1)) {
2621 int er = proc_sock_errno();
2622 if (er == SOCEINTR)
2623 goto again;
2624 internal(file_line, "select returned an error: %d", er);
2626 return !!r;
2629 * os_read/os_write is non-blocking even for standard handles,
2630 * so we don't need this function
2632 return true;
2636 struct proc_handle {
2637 struct tree_entry entry;
2638 PID pid;
2639 bool fired;
2640 bool detached;
2641 RESULTCODES codes;
2642 struct list wait_list;
2645 static struct tree proc_tree;
2646 static mutex_t proc_tree_mutex;
2647 static TID proc_wait_thread;
2649 static inline void proc_lock(void)
2651 mutex_lock(&proc_tree_mutex);
2654 static inline void proc_unlock(void)
2656 mutex_unlock(&proc_tree_mutex);
2659 static int proc_handle_compare(const struct tree_entry *e, uintptr_t pid)
2661 const struct proc_handle *ph = get_struct(e, struct proc_handle, entry);
2662 if (unlikely(ph->pid == (PID)pid)) return 0;
2663 if (ph->pid > (PID)pid) return 1;
2664 return -1;
2667 static bool proc_addstr(char **ptr, size_t *len, const char *str, bool cvt_slashes, ajla_error_t *err)
2669 size_t i, j, bs;
2670 bool quote = false;
2671 if (*len) {
2672 if ((*ptr)[*len - 1]) {
2673 if (unlikely(!array_add_mayfail(char, ptr, len, ' ', NULL, err)))
2674 return false;
2676 if (!str[0] || str[strcspn(str, " \t")])
2677 quote = true;
2679 if (quote) {
2680 if (unlikely(!array_add_mayfail(char, ptr, len, '"', NULL, err)))
2681 return false;
2683 bs = 0;
2684 for (i = 0; str[i]; i++) {
2685 char c = str[i];
2686 if (cvt_slashes && c == '/')
2687 c = '\\';
2688 if (c == '\\') {
2689 bs++;
2690 } else if (c == '"') {
2691 for (j = 0; j <= bs; j++)
2692 if (unlikely(!array_add_mayfail(char, ptr, len, '\\', NULL, err)))
2693 return false;
2694 bs = 0;
2695 } else {
2696 bs = 0;
2698 if (unlikely(!array_add_mayfail(char, ptr, len, c, NULL, err)))
2699 return false;
2701 if (quote) {
2702 for (j = 0; j < bs; j++)
2703 if (unlikely(!array_add_mayfail(char, ptr, len, '\\', NULL, err)))
2704 return false;
2705 if (unlikely(!array_add_mayfail(char, ptr, len, '"', NULL, err)))
2706 return false;
2708 return true;
2711 static void os2_wait_thread(ULONG attr_unused x)
2713 sig_state_t s;
2715 os_block_signals(&s);
2716 lock_loop:
2717 proc_lock();
2719 while (!tree_is_empty(&proc_tree)) {
2720 RESULTCODES codes;
2721 PID pid;
2722 APIRET rc;
2723 struct tree_entry *e;
2724 struct proc_handle *ph;
2726 proc_unlock();
2727 os_unblock_signals(&s);
2729 rc = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &codes, &pid, 0);
2730 if (unlikely(rc))
2731 internal(file_line, "DosWaitChild returned an error: %lu", rc);
2733 os_block_signals(&s);
2734 proc_lock();
2736 e = tree_find(&proc_tree, proc_handle_compare, pid);
2737 if (!e) {
2738 continue;
2741 ph = get_struct(e, struct proc_handle, entry);
2742 ph->fired = true;
2743 ph->codes = codes;
2744 tree_delete(&ph->entry);
2746 if (!ph->detached) {
2747 call(wake_up_wait_list)(&ph->wait_list, &proc_tree_mutex, false);
2748 goto lock_loop;
2749 } else {
2750 os2_free_buffer(ph);
2753 proc_wait_thread = 0;
2755 proc_unlock();
2756 os_unblock_signals(&s);
2759 struct proc_handle *os_proc_spawn(dir_handle_t wd, const char *path, size_t n_handles, handle_t *source, int *target, char * const args[], char *envc, ajla_error_t *err)
2761 char * const *a;
2762 char *ptr, *copy_of_ptr, *copy_of_env;
2763 size_t len, env_len;
2764 char obj_name_buf[260];
2765 RESULTCODES codes;
2766 APIRET rc;
2767 HFILE i;
2768 ajla_error_t err_x, err_xx;
2769 struct proc_handle *ph;
2770 struct tree_entry *en;
2771 struct tree_insert_position ins;
2772 unsigned char *cwd;
2773 short mapping_table[OS2_MAX_HANDLE];
2775 if (!*args) {
2776 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "empty arguments in spawn");
2777 return NULL;
2780 if (unlikely(!array_init_mayfail(char, &ptr, &len, err)))
2781 return NULL;
2783 for (a = args; *a; a++) {
2784 if (!proc_addstr(&ptr, &len, *a, a == args, err))
2785 return NULL;
2786 if (a == args)
2787 if (unlikely(!array_add_mayfail(char, &ptr, &len, 0, NULL, err)))
2788 return NULL;
2791 if (unlikely(!array_add_mayfail(char, &ptr, &len, 0, NULL, err)))
2792 return NULL;
2793 if (unlikely(!array_add_mayfail(char, &ptr, &len, 0, NULL, err)))
2794 return NULL;
2796 if (unlikely(len >= 65536)) {
2797 mem_free(ptr);
2798 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "arguments size overflow");
2799 return NULL;
2803 * The EMX source code says that the argument buffer must not cross
2804 * 64k boundary.
2806 copy_of_ptr = os2_alloc_buffer(len);
2807 if (unlikely(!copy_of_ptr)) {
2808 mem_free(ptr);
2809 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for arg buffer (%"PRIuMAX" bytes)", (uintmax_t)len);
2810 return NULL;
2812 memcpy(copy_of_ptr, ptr, len);
2813 mem_free(ptr);
2815 for (env_len = 0; envc[env_len];)
2816 env_len += strlen(envc + env_len) + 1;
2817 env_len++;
2818 copy_of_env = os2_alloc_buffer(env_len);
2819 if (unlikely(!copy_of_env)) {
2820 os2_free_buffer(copy_of_ptr);
2821 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for env buffer (%"PRIuMAX" bytes)", (uintmax_t)env_len);
2822 return NULL;
2824 memcpy(copy_of_env, envc, env_len);
2826 ph = os2_alloc_buffer(sizeof(struct proc_handle));
2827 if (unlikely(!ph)) {
2828 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_OUT_OF_MEMORY), err, "out of memory for process handle (%"PRIuMAX" bytes)", (uintmax_t)sizeof(struct proc_handle));
2829 os2_free_buffer(copy_of_ptr);
2830 os2_free_buffer(copy_of_env);
2831 return NULL;
2833 ph->fired = false;
2834 ph->detached = false;
2835 list_init(&ph->wait_list);
2837 cwd = os_dir_cwd(err);
2838 if (unlikely(!cwd)) {
2839 os2_free_buffer(copy_of_ptr);
2840 os2_free_buffer(copy_of_env);
2841 return NULL;
2844 proc_lock();
2846 if (!proc_wait_thread) {
2847 mutex_lock(&thread_spawn_mutex);
2848 rc = DosCreateThread(&proc_wait_thread, os2_wait_thread, 0, 0x2, OS2_IO_THREAD_STACK_SIZE);
2849 mutex_unlock(&thread_spawn_mutex);
2850 if (unlikely(rc != 0)) {
2851 ajla_error_t e = error_from_os2(EC_SYSCALL, rc);
2852 fatal_mayfail(e, err, "can't spawn wait thread: %s", error_decode(e));
2853 proc_unlock();
2854 mem_free(cwd);
2855 os2_free_buffer(copy_of_ptr);
2856 os2_free_buffer(copy_of_env);
2857 return NULL;
2861 for (i = 0; i < OS2_MAX_HANDLE; i++)
2862 mapping_table[i] = -1;
2864 os2_enter_critical_section();
2866 for (i = 0; i < n_handles; i++) {
2867 handle_t s = source[i];
2868 HFILE tgt;
2869 if (unlikely(s->t == HANDTYPE_SOCKET)) {
2870 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), &err_x, "socket can't be inherited by subprocesses on OS/2");
2871 goto handle_error;
2873 address_lock(s, DEPTH_THUNK);
2874 if (s->rd.thread) {
2875 os2_terminate_io_thread(&s->rd);
2877 if (s->ms.thread) {
2878 os2_terminate_io_thread(&s->ms);
2880 address_unlock(s, DEPTH_THUNK);
2881 tgt = target[i];
2882 if (tgt >= OS2_MAX_HANDLE) {
2883 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), &err_x, "destination handle too large");
2884 goto handle_error;
2886 if (!os2_handle_is_valid(tgt)) {
2887 /*debug("placeholder");*/
2888 if (unlikely(!os2_handle_placeholder(tgt, &err_x)))
2889 goto handle_error;
2890 mapping_table[tgt] = -2;
2893 for (i = 0; i < n_handles; i++) {
2894 HFILE tgt = target[i];
2895 if (mapping_table[tgt] == -1) {
2896 HFILE n;
2897 if (unlikely(!os2_handle_dup1(tgt, &n, &err_x)))
2898 goto handle_error;
2899 mapping_table[tgt] = n;
2900 /*debug("duplicating %lu -> %lu", tgt, n);*/
2903 for (i = 0; i < n_handles; i++) {
2904 HFILE src = source[i]->h;
2905 HFILE tgt = target[i];
2906 if (mapping_table[src] >= 0)
2907 src = mapping_table[src];
2908 /*debug("dup2: %lu -> %u", src, tgt);*/
2909 if (unlikely(!os2_handle_dup2(src, tgt, &err_x)))
2910 goto handle_error;
2911 os2_set_inherit(tgt, true);
2914 if (unlikely(!os2_dir_set(wd, err))) {
2915 os2_exit_critical_section();
2916 proc_unlock();
2917 mem_free(cwd);
2918 os2_free_buffer(copy_of_ptr);
2919 os2_free_buffer(copy_of_env);
2920 return NULL;
2923 rc = DosExecPgm(obj_name_buf, sizeof obj_name_buf, EXEC_ASYNCRESULT, copy_of_ptr, copy_of_env, &codes, path);
2925 if (0) {
2926 handle_error:
2927 rc = (APIRET)-1;
2930 for (i = 0; i < n_handles; i++) {
2931 if (mapping_table[i] == -2)
2932 os2_close_handle(target[i]);
2934 for (i = 0; i < OS2_MAX_HANDLE; i++) {
2935 if (mapping_table[i] >= 0) {
2936 os2_handle_dup2(mapping_table[i], i, NULL);
2937 os2_close_handle(mapping_table[i]);
2940 /*for (i = 0; i < OS2_MAX_HANDLE; i++) {
2941 if (mapping_table[i] >= 0) {
2942 debug("restored: %u -> %u", mapping_table[i], i);
2946 os2_dir_set(cwd, &err_xx);
2948 os2_exit_critical_section();
2950 mem_free(cwd);
2951 os2_free_buffer(copy_of_ptr);
2952 os2_free_buffer(copy_of_env);
2954 if (unlikely(rc != 0)) {
2955 ajla_error_t e;
2956 proc_unlock();
2957 if (rc != (APIRET)-1) {
2958 e = error_from_os2(EC_SYSCALL, rc);
2959 fatal_mayfail(e, err, "DosExecPgm returned(%s) an error: %s", path, error_decode(e));
2960 } else {
2961 e = err_x;
2962 fatal_mayfail(e, err, "error preparing handles for spawn: %s", error_decode(e));
2964 return NULL;
2967 ph->pid = codes.codeTerminate;
2969 en = tree_find_for_insert(&proc_tree, proc_handle_compare, ph->pid, &ins);
2970 if (unlikely(en != NULL)) {
2971 fatal("pid %ld is already present in the tree", (long)ph->pid);
2974 tree_insert_after_find(&ph->entry, &ins);
2976 proc_unlock();
2978 return ph;
2981 void os_proc_free_handle(struct proc_handle *ph)
2983 proc_lock();
2984 ajla_assert_lo(list_is_empty(&ph->wait_list), (file_line, "os_proc_free_handle: freeing handle when there are processes waiting for it"));
2985 if (ph->fired) {
2986 proc_unlock();
2987 os2_free_buffer(ph);
2988 } else {
2989 ph->detached = true;
2990 proc_unlock();
2994 bool os_proc_register_wait(struct proc_handle *ph, mutex_t **mutex_to_lock, struct list *list_entry, int *status)
2996 proc_lock();
2997 if (ph->fired) {
2998 /*debug("exit: %lu, %lu", ph->codes.codeTerminate, ph->codes.codeResult);*/
2999 *status = ph->codes.codeResult;
3000 proc_unlock();
3001 return true;
3002 } else {
3003 *mutex_to_lock = &proc_tree_mutex;
3004 list_add(&ph->wait_list, list_entry);
3005 proc_unlock();
3006 return false;
3011 #define N_SIG 5
3013 static mutex_t signal_mutex;
3015 static bool signal_thread_running;
3016 static thread_t signal_thread;
3017 static HEV signal_ev;
3019 static struct {
3020 thread_volatile signal_seq_t sig_sequence;
3021 signal_seq_t last_sig_sequence;
3022 thread_volatile uintptr_t refcount;
3023 struct list wait_list;
3024 } signals[N_SIG];
3026 ULONG APIENTRY os2_exception_handler(PEXCEPTIONREPORTRECORD exrep, struct _EXCEPTIONREGISTRATIONRECORD attr_unused *expreg, PCONTEXTRECORD attr_unused ctx, PVOID attr_unused v)
3028 /*debug("signal: %lx, %lx", exrep->ExceptionNum, exrep->ExceptionInfo[0]);*/
3029 if (exrep->ExceptionNum == XCPT_SIGNAL) {
3030 unsigned sig = exrep->ExceptionInfo[0];
3031 if (sig < N_SIG) {
3032 if (signals[sig].refcount) {
3033 APIRET rc;
3034 signals[sig].sig_sequence++;
3035 rc = DosPostEventSem(signal_ev);
3036 if (unlikely(rc != 0) && unlikely(rc != ERROR_ALREADY_POSTED) && rc != ERROR_TOO_MANY_POSTS)
3037 internal(file_line, "DosPostEventSem returned an error: %lu", rc);
3038 return XCPT_CONTINUE_EXECUTION;
3042 return XCPT_CONTINUE_SEARCH;
3045 static void signal_thread_fn(void attr_unused *p)
3047 ULONG count;
3048 APIRET rc;
3049 int sig;
3050 scan_again:
3051 rc = DosResetEventSem(signal_ev, &count);
3052 if (rc && unlikely(rc != ERROR_ALREADY_RESET))
3053 internal(file_line, "DosResetEventSem returned an error: %lu", rc);
3054 sig = 0;
3055 again:
3056 mutex_lock(&signal_mutex);
3057 for (; sig < N_SIG; sig++) {
3058 signal_seq_t seq = signals[sig].sig_sequence;
3059 if (signals[sig].last_sig_sequence != seq) {
3060 signals[sig].last_sig_sequence = seq;
3061 call(wake_up_wait_list)(&signals[sig].wait_list, &signal_mutex, true);
3062 sig++;
3063 goto again;
3066 mutex_unlock(&signal_mutex);
3067 if (unlikely(!signal_thread_running))
3068 return;
3069 rc = DosWaitEventSem(signal_ev, SEM_INDEFINITE_WAIT);
3070 if (unlikely(rc != 0)) {
3071 if (rc == ERROR_INTERRUPT || rc == ERROR_TIMEOUT)
3072 goto scan_again;
3073 internal(file_line, "DosWaitEventSem returned an error: %lu", rc);
3075 goto scan_again;
3078 int os_signal_handle(const char *str, signal_seq_t *seq, ajla_error_t *err)
3080 int sig;
3081 if (!strcmp(str, "SIGINT")) {
3082 sig = XCPT_SIGNAL_INTR;
3083 } else if (!strcmp(str, "SIGBREAK")) {
3084 sig = XCPT_SIGNAL_BREAK;
3085 } else if (!strcmp(str, "SIGTERM")) {
3086 sig = XCPT_SIGNAL_KILLPROC;
3087 } else {
3088 *seq = 0;
3089 return 0;
3091 mutex_lock(&signal_mutex);
3092 *seq = signals[sig].last_sig_sequence;
3093 signals[sig].refcount++;
3094 if (!signal_thread_running) {
3095 signal_thread_running = true;
3096 if (unlikely(!thread_spawn(&signal_thread, signal_thread_fn, NULL, PRIORITY_IO, err))) {
3097 signal_thread_running = false;
3098 signals[sig].refcount--;
3099 mutex_unlock(&signal_mutex);
3100 return -1;
3103 mutex_unlock(&signal_mutex);
3104 return sig;
3107 void os_signal_unhandle(int sig)
3109 if (!sig)
3110 return;
3111 mutex_lock(&signal_mutex);
3112 ajla_assert_lo(signals[sig].refcount > 0, (file_line, "os_signal_unhandle: refcount underflow"));
3113 signals[sig].refcount--;
3114 mutex_unlock(&signal_mutex);
3117 signal_seq_t os_signal_seq(int sig)
3119 signal_seq_t seq;
3120 if (!sig)
3121 return 0;
3122 mutex_lock(&signal_mutex);
3123 seq = signals[sig].last_sig_sequence;
3124 mutex_unlock(&signal_mutex);
3125 return seq;
3128 bool os_signal_wait(int sig, signal_seq_t seq, mutex_t **mutex_to_lock, struct list *list_entry)
3130 if (!sig) {
3131 iomux_never(mutex_to_lock, list_entry);
3132 return true;
3134 mutex_lock(&signal_mutex);
3135 if (seq != signals[sig].last_sig_sequence) {
3136 mutex_unlock(&signal_mutex);
3137 return false;
3139 *mutex_to_lock = &signal_mutex;
3140 list_add(&signals[sig].wait_list, list_entry);
3141 mutex_unlock(&signal_mutex);
3142 return true;
3146 static int os2_notify_socket[2];
3148 static bool os2_socketpair_af_unix(int result[2])
3150 int lst;
3151 struct sockaddr_un sun;
3152 socklen_t len;
3153 int r;
3154 int one;
3156 lst = -1;
3157 result[0] = result[1] = -1;
3159 lst = proc_socket(PF_UNIX, SOCK_STREAM, 0);
3160 if (unlikely(lst == -1))
3161 goto fail;
3163 memset(&sun, 0, sizeof sun);
3164 sun.sun_family = AF_UNIX;
3165 r = proc_bind(lst, (struct sockaddr *)&sun, sizeof sun);
3166 if (unlikely(r == -1))
3167 goto fail;
3169 len = sizeof sun;
3170 r = proc_getsockname(lst, (struct sockaddr *)&sun, &len);
3171 if (unlikely(r == -1))
3172 goto fail;
3174 r = proc_listen(lst, 1);
3175 if (unlikely(r == -1))
3176 goto fail;
3178 result[0] = proc_socket(PF_UNIX, SOCK_STREAM, 0);
3179 if (unlikely(result[0] == -1))
3180 goto fail;
3182 r = proc_connect(result[0], (struct sockaddr *)&sun, sizeof sun);
3183 if (unlikely(r == -1))
3184 goto fail;
3186 len = sizeof sun;
3187 result[1] = proc_accept(lst, (struct sockaddr *)&sun, &len);
3188 if (unlikely(result[1] == -1))
3189 goto fail;
3191 one = 1;
3192 r = proc_ioctl(result[0], FIONBIO, &one, sizeof one);
3193 if (unlikely(r == -1))
3194 goto fail;
3195 r = proc_ioctl(result[1], FIONBIO, &one, sizeof one);
3196 if (unlikely(r == -1))
3197 goto fail;
3199 proc_soclose(lst);
3201 return true;
3203 fail:
3204 if (lst != -1)
3205 proc_soclose(lst);
3206 if (result[0] != -1)
3207 proc_soclose(result[0]);
3208 if (result[1] != -1)
3209 proc_soclose(result[1]);
3210 return false;
3213 static bool os2_socketpair(int result[2])
3215 int lst;
3216 struct sockaddr_in sin;
3217 socklen_t len;
3218 int r;
3219 int one;
3221 if (os2_socketpair_af_unix(result))
3222 return true;
3224 lst = -1;
3225 result[0] = result[1] = -1;
3227 lst = proc_socket(PF_INET, SOCK_STREAM, 0);
3228 if (unlikely(lst == -1))
3229 goto fail;
3231 memset(&sin, 0, sizeof sin);
3232 sin.sin_family = AF_INET;
3233 r = proc_bind(lst, (struct sockaddr *)&sin, sizeof sin);
3234 if (unlikely(r == -1))
3235 goto fail;
3237 len = sizeof sin;
3238 r = proc_getsockname(lst, (struct sockaddr *)&sin, &len);
3239 if (unlikely(r == -1))
3240 goto fail;
3242 r = proc_listen(lst, 1);
3243 if (unlikely(r == -1))
3244 goto fail;
3246 result[0] = proc_socket(PF_INET, SOCK_STREAM, 0);
3247 if (unlikely(result[0] == -1))
3248 goto fail;
3250 r = proc_connect(result[0], (struct sockaddr *)&sin, sizeof sin);
3251 if (unlikely(r == -1))
3252 goto fail;
3254 len = sizeof sin;
3255 result[1] = proc_accept(lst, (struct sockaddr *)&sin, &len);
3256 if (unlikely(result[1] == -1))
3257 goto fail;
3259 one = 1;
3260 r = proc_ioctl(result[0], FIONBIO, &one, sizeof one);
3261 if (unlikely(r == -1))
3262 goto fail;
3263 r = proc_ioctl(result[1], FIONBIO, &one, sizeof one);
3264 if (unlikely(r == -1))
3265 goto fail;
3267 proc_soclose(lst);
3269 return true;
3271 fail:
3272 if (lst != -1)
3273 proc_soclose(lst);
3274 if (result[0] != -1)
3275 proc_soclose(result[0]);
3276 if (result[1] != -1)
3277 proc_soclose(result[1]);
3278 return false;
3281 static void os2_notify(void)
3283 int r;
3284 char c = 0;
3285 r = proc_send(os2_notify_socket[1], &c, 1, 0);
3286 if (unlikely(r == -1)) {
3287 int er = proc_sock_errno();
3288 if (er != SOCEAGAIN)
3289 fatal("error writing to the notify socket: %d", er);
3293 static bool os2_drain_notify_pipe(void)
3295 static char buffer[1024];
3296 int r;
3297 r = proc_recv(os2_notify_socket[0], buffer, sizeof buffer, 0);
3298 if (likely(r == -1)) {
3299 int er = proc_sock_errno();
3300 if (likely(er == SOCEAGAIN))
3301 return false;
3302 fatal("error reading the notify socket: %d", er);
3304 return !r;
3307 static void os2_shutdown_notify_pipe(void)
3309 int r;
3310 r = proc_shutdown(os2_notify_socket[0], 2);
3311 if (likely(r == -1)) {
3312 int er = errno;
3313 fatal("error shutting down the notify socket: %d", er);
3315 #ifdef DEBUG
3316 os2_notify();
3317 #endif
3321 handle_t os_socket(int domain, int type, int protocol, ajla_error_t *err)
3323 int sock;
3324 if (unlikely(!tcpip_loaded)) {
3325 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not installed");
3326 return NULL;
3328 domain = os_socket_pf(domain, err);
3329 if (unlikely(domain == -1))
3330 return NULL;
3331 type = os_socket_type(type, err);
3332 if (unlikely(type == -1))
3333 return NULL;
3334 sock = proc_socket(domain, type, protocol);
3335 if (unlikely(sock == -1)) {
3336 fatal_mayfail(error_from_os2_socket(), err, "socket failed");
3337 return NULL;
3339 return os2_socket_to_handle(sock, err);
3342 bool os_bind_connect(bool bnd, handle_t h, unsigned char *addr, size_t addr_len, ajla_error_t *err)
3344 int r;
3345 int er;
3346 struct sockaddr *sa;
3348 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3349 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3350 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3351 return false;
3353 again:
3354 sa = os_get_sock_addr(addr, &addr_len, err);
3355 if (unlikely(!sa))
3356 return false;
3357 r = (likely(!bnd) ? proc_connect : proc_bind)(h->h, sa, addr_len);
3358 mem_free_aligned(sa);
3359 if (unlikely(!r))
3360 return true;
3361 er = proc_sock_errno();
3362 if (er == SOCEINTR)
3363 goto again;
3364 if (likely(!bnd) && likely(er == SOCEINPROGRESS))
3365 return true;
3366 fatal_mayfail(error_from_os2_socket(), err, "can't %s socket: %d", !bnd ? "connect" : "bind", er);
3367 return false;
3370 bool os_connect_completed(handle_t h, ajla_error_t *err)
3372 int r;
3373 int er;
3374 socklen_t er_l = sizeof er;
3376 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3377 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3378 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3379 return false;
3382 r = proc_getsockopt(h->h, SOL_SOCKET, SO_ERROR, &er, &er_l);
3383 if (unlikely(r == -1)) {
3384 fatal_mayfail(error_from_os2_socket(), err, "getsockopt returned an error: %d", proc_sock_errno());
3385 return false;
3387 if (unlikely(er)) {
3388 ajla_error_t e = error_ajla_aux(EC_SYSCALL, AJLA_ERROR_OS2_SOCKET, er);
3389 fatal_mayfail(e, err, "can't connect socket: %d", er);
3390 return false;
3392 return true;
3395 bool os_listen(handle_t h, ajla_error_t *err)
3397 int r;
3399 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3400 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3401 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3402 return false;
3405 r = proc_listen(h->h, signed_maximum(int));
3406 if (unlikely(r == -1)) {
3407 fatal_mayfail(error_from_os2_socket(), err, "listen returned an error: %d", proc_sock_errno());
3408 return false;
3410 return true;
3413 int os_accept(handle_t h, handle_t *result, ajla_error_t *err)
3415 int r;
3417 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3418 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3419 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3420 return OS_RW_ERROR;
3423 again:
3424 r = proc_accept(h->h, NULL, 0);
3425 if (unlikely(r == -1)) {
3426 int er = proc_sock_errno();
3427 if (er == SOCEINTR)
3428 goto again;
3429 if (er == SOCEAGAIN)
3430 return OS_RW_WOULDBLOCK;
3431 fatal_mayfail(error_from_os2_socket(), err, "accept returned an error: %d", er);
3432 return OS_RW_ERROR;
3435 *result = os2_socket_to_handle(r, err);
3437 return unlikely(*result == NULL) ? OS_RW_ERROR : 0;
3440 bool os_getsockpeername(bool peer, handle_t h, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
3442 int r;
3443 struct sockaddr *sa;
3444 socklen_t addrlen;
3446 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3447 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3448 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3449 return false;
3452 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
3453 if (unlikely(!sa))
3454 return false;
3455 addrlen = SOCKADDR_MAX_LEN;
3457 r = (!peer ? proc_getsockname : proc_getpeername)(h->h, sa, &addrlen);
3458 if (r == -1) {
3459 int er = proc_sock_errno();
3460 fatal_mayfail(error_from_os2_socket(), err, "%s returned an error: %d", !peer ? "getsockname" : "getpeername", er);
3461 goto free_ret_false;
3464 *addr = os_get_ajla_addr(sa, &addrlen, err);
3465 if (unlikely(!*addr))
3466 goto free_ret_false;
3468 *addr_len = addrlen;
3470 mem_free_aligned(sa);
3471 return true;
3473 free_ret_false:
3474 mem_free_aligned(sa);
3475 return false;
3478 ssize_t os_recvfrom(handle_t h, char *buffer, size_t len, int flags, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
3480 int r, f;
3481 struct sockaddr *sa;
3482 socklen_t addrlen;
3484 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3485 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3486 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3487 return OS_RW_ERROR;
3490 f = translate_flags(os_socket_msg, flags, err);
3491 if (unlikely(f < 0))
3492 return OS_RW_ERROR;
3494 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
3495 if (unlikely(!sa))
3496 return OS_RW_ERROR;
3497 again:
3498 addrlen = SOCKADDR_MAX_LEN;
3499 r = proc_recvfrom(h->h, buffer, len, f, sa, &addrlen);
3500 if (unlikely(r == -1)) {
3501 int er = proc_sock_errno();
3502 if (er == SOCEINTR)
3503 goto again;
3504 if (er == SOCEAGAIN) {
3505 mem_free_aligned(sa);
3506 return OS_RW_WOULDBLOCK;
3508 fatal_mayfail(error_from_os2_socket(), err, "recvfrom returned an error: %d", er);
3509 goto free_ret_error;
3511 if (unlikely(addrlen > SOCKADDR_MAX_LEN)) {
3512 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "the system returned too long address");
3513 goto free_ret_error;
3516 if (!addrlen) {
3517 if (unlikely(!array_init_mayfail(unsigned char, addr, addr_len, err))) {
3518 goto free_ret_error;
3520 } else {
3521 *addr = os_get_ajla_addr(sa, &addrlen, err);
3522 if (unlikely(!*addr))
3523 goto free_ret_error;
3524 *addr_len = addrlen;
3527 mem_free_aligned(sa);
3528 return r;
3530 free_ret_error:
3531 mem_free_aligned(sa);
3532 return OS_RW_ERROR;
3535 ssize_t os_sendto(handle_t h, const char *buffer, size_t len, int flags, unsigned char *addr, size_t addr_len, ajla_error_t *err)
3537 int r, f;
3538 struct sockaddr *sa;
3540 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3541 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3542 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3543 return OS_RW_ERROR;
3546 f = translate_flags(os_socket_msg, flags, err);
3547 if (unlikely(f < 0))
3548 return OS_RW_ERROR;
3550 again:
3551 if (addr_len != 0) {
3552 size_t al = addr_len;
3553 sa = os_get_sock_addr(addr, &al, err);
3554 if (unlikely(!sa))
3555 return OS_RW_ERROR;
3556 r = proc_sendto(h->h, buffer, len, f, sa, al);
3557 mem_free_aligned(sa);
3558 } else {
3559 r = proc_send(h->h, buffer, len, f);
3562 if (unlikely(r == -1)) {
3563 int er = proc_sock_errno();
3564 if (er == SOCEINTR)
3565 goto again;
3566 if (er == SOCEAGAIN)
3567 return OS_RW_WOULDBLOCK;
3568 fatal_mayfail(error_from_os2_socket(), err, "send%s returned an error: %d", addr_len ? "to" : "", er);
3569 return OS_RW_ERROR;
3572 return r;
3575 bool os_getsockopt(handle_t h, int level, int option, char **buffer, size_t *buffer_len, ajla_error_t *err)
3577 int r;
3578 socklen_t opt_len;
3580 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3581 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3582 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3583 return false;
3586 level = os_socket_level(level, err);
3587 if (unlikely(level < 0))
3588 return false;
3590 option = os_socket_option(option, err);
3591 if (unlikely(level < 0))
3592 return false;
3594 opt_len = 4096;
3596 *buffer = mem_alloc_mayfail(char *, opt_len, err);
3597 if (unlikely(!*buffer))
3598 return false;
3600 r = proc_getsockopt(h->h, level, option, *buffer, &opt_len);
3602 if (unlikely(r == -1)) {
3603 int er = proc_sock_errno();
3604 fatal_mayfail(error_from_os2_socket(), err, "getsockopt returned an error: %d", er);
3605 mem_free(*buffer);
3606 return false;
3609 *buffer_len = opt_len;
3610 return true;
3613 bool os_setsockopt(handle_t h, int level, int option, const char *buffer, size_t buffer_len, ajla_error_t *err)
3615 int r;
3617 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3618 if (unlikely(h->t != HANDTYPE_SOCKET)) {
3619 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3620 return false;
3623 level = os_socket_level(level, err);
3624 if (unlikely(level < 0))
3625 return false;
3627 option = os_socket_option(option, err);
3628 if (unlikely(level < 0))
3629 return false;
3631 r = proc_setsockopt(h->h, level, option, buffer, buffer_len);
3633 if (unlikely(r == -1)) {
3634 int er = proc_sock_errno();
3635 fatal_mayfail(error_from_os2_socket(), err, "setsockopt returned an error: %d", er);
3636 return false;
3639 return true;
3642 bool os_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
3644 struct hostent *he;
3645 size_t i;
3646 void *xresult;
3647 char *a;
3649 if (unlikely(!tcpip_loaded)) {
3650 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not installed");
3651 return false;
3654 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
3655 return false;
3657 he = proc_gethostbyname(host);
3659 if (unlikely(!he)) {
3660 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, *proc_h_errno), err, "host not found");
3661 goto fail;
3664 if (he->h_addrtype != AF_INET || he->h_length != 4 || !he->h_addr) {
3665 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, NO_DATA), err, "host not found");
3666 goto fail;
3669 for (i = 0; (a = he->h_addr_list[i]); i++) {
3670 struct sockaddr_in sin;
3671 struct sockaddr sa;
3672 struct address addr;
3673 ajla_error_t e;
3674 socklen_t addrlen = sizeof sin;
3676 sin.sin_family = AF_INET;
3677 sin.sin_port = (port << 8) | (port >> 8);
3678 memcpy(&sin.sin_addr, a, 4);
3680 memcpy(&sa, &sin, sizeof sin);
3682 addr.address = os_get_ajla_addr(&sa, &addrlen, &e);
3683 if (unlikely(!addr.address))
3684 continue;
3685 addr.address_length = addrlen;
3687 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
3688 *result = xresult;
3689 goto fail;
3693 if (unlikely(!*result_l)) {
3694 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, NO_DATA), err, "host not found");
3695 goto fail;
3698 return true;
3700 fail:
3701 for (i = 0; i < *result_l; i++)
3702 mem_free((*result)[i].address);
3703 mem_free(*result);
3704 return false;
3707 char *os_getnameinfo(unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3709 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "getnameinfo not supported");
3710 return NULL;
3713 const char *os_decode_error(ajla_error_t attr_unused error, char attr_unused *(*tls_buffer)(void))
3715 return NULL;
3719 static struct dl_handle_t *hdoscalls = NULL;
3720 static struct dl_handle_t *hso32dll = NULL;
3721 static struct dl_handle_t *htcp32dll = NULL;
3723 struct dl_handle_t *os_dlopen(const char *filename, ajla_error_t *err, char **err_msg)
3725 char mod[9];
3726 HMODULE h;
3727 APIRET rc;
3728 again:
3729 memset(mod, 0, sizeof mod);
3730 rc = DosLoadModule(mod, sizeof mod, filename, &h);
3731 _control87(CW_DEFAULT, 0xffff);
3732 if (unlikely(rc != 0)) {
3733 ajla_error_t e;
3734 if (rc == ERROR_INTERRUPT)
3735 goto again;
3736 if (err_msg)
3737 *err_msg = NULL;
3738 e = error_from_os2(EC_SYSCALL, rc);
3739 fatal_mayfail(e, err, "can't load library '%s': %s (%s)", filename, error_decode(e), mod);
3740 return NULL;
3743 if (unlikely(!h))
3744 fatal("DosLoadModule returned zero");
3745 /*debug("loaded: %s -> %lu", filename, h);*/
3746 return num_to_ptr(h);
3749 void os_dlclose(struct dl_handle_t *dlh)
3751 APIRET rc;
3752 again:
3753 rc = DosFreeModule(ptr_to_num(dlh));
3754 _control87(CW_DEFAULT, 0xffff);
3755 if (unlikely(rc != 0)) {
3756 if (rc == ERROR_INTERRUPT)
3757 goto again;
3758 internal(file_line, "DosFreeModule returned an error: %lu", rc);
3762 static const struct {
3763 const char *name;
3764 unsigned ordinal;
3765 } ordinals[] = {
3766 #include "os_os2_s.inc"
3769 bool os_dlsym(struct dl_handle_t *dlh, const char *symbol, void **result)
3771 APIRET rc;
3772 if (dlh == hdoscalls) {
3773 size_t r;
3774 binary_search(size_t, n_array_elements(ordinals), r, !strcmp(ordinals[r].name, symbol), strcmp(ordinals[r].name, symbol) < 0, return false);
3775 rc = DosQueryProcAddr(ptr_to_num(dlh), ordinals[r].ordinal, NULL, (PPFN)result);
3776 /*debug("symbol %s, ordinal %u, result %lu", symbol, ordinals[r].ordinal, rc);*/
3777 } else {
3778 rc = DosQueryProcAddr(ptr_to_num(dlh), 0, symbol, (PPFN)result);
3779 /*debug("symbol %s, result %lu", symbol, rc);*/
3781 if (unlikely(rc != 0)) {
3782 *result = NULL;
3783 return false;
3785 return true;
3789 void os_code_invalidate_cache(uint8_t attr_unused *code_ptr, size_t attr_unused code_size, bool attr_unused set_exec)
3793 void *os_code_map(uint8_t *code, size_t attr_unused code_size, ajla_error_t attr_unused *err)
3795 return code;
3798 void os_code_unmap(void *mapped_code, size_t attr_unused code_size)
3800 mem_free(mapped_code);
3804 static int compare_int(const void *x1, const void *x2)
3806 return *cast_ptr(const int *, x1) - *cast_ptr(const int *, x2);
3809 static thread_t iomux_thread;
3811 thread_function_decl(iomux_poll_thread,
3812 thread_set_id(-1);
3813 while (likely(!os2_drain_notify_pipe())) {
3814 int *select_arg;
3815 size_t select_arg_n;
3816 size_t select_arg_read = 0;
3817 int wr;
3818 int r;
3820 array_init(int, &select_arg, &select_arg_n);
3821 array_add(int, &select_arg, &select_arg_n, os2_notify_socket[0]);
3822 mutex_lock(&socket_list_mutex);
3823 for (wr = 0; wr < 2; wr++) {
3824 struct list *l;
3825 list_for_each(l, &socket_list[wr]) {
3826 struct os2_io_thread *thr = get_struct(l, struct os2_io_thread, socket_entry);
3827 handle_t h = thr->h;
3828 address_lock(h, DEPTH_THUNK);
3829 if (!list_is_empty(&thr->wait_list)) {
3830 array_add(int, &select_arg, &select_arg_n, h->h);
3831 } else {
3832 l = l->prev;
3833 list_del(&thr->socket_entry);
3834 thr->socket_entry.next = NULL;
3836 address_unlock(h, DEPTH_THUNK);
3838 if (!wr)
3839 select_arg_read = select_arg_n;
3841 mutex_unlock(&socket_list_mutex);
3843 r = proc_select(select_arg, select_arg_read, select_arg_n - select_arg_read, 0, -1);
3845 if (unlikely(r == -1)) {
3846 int er = proc_sock_errno();
3847 if (er == SOCEINTR) {
3848 mem_free(select_arg);
3849 continue;
3851 internal(file_line, "select returned an error: %d", er);
3854 qsort(select_arg, select_arg_read, sizeof(int), compare_int);
3855 qsort(select_arg + select_arg_read, select_arg_n - select_arg_read, sizeof(int), compare_int);
3857 mutex_lock(&socket_list_mutex);
3858 for (wr = 0; wr < 2; wr++) {
3859 struct list *l;
3860 list_for_each(l, &socket_list[wr]) {
3861 struct os2_io_thread *thr = get_struct(l, struct os2_io_thread, socket_entry);
3862 handle_t h = thr->h;
3863 int hndl = h->h;
3864 void *p;
3865 if (!wr)
3866 p = bsearch(&hndl, select_arg, select_arg_read, sizeof(int), compare_int);
3867 else
3868 p = bsearch(&hndl, select_arg + select_arg_read, select_arg_n - select_arg_read, sizeof(int), compare_int);
3869 if (p) {
3870 address_lock(h, DEPTH_THUNK);
3871 call(wake_up_wait_list)(&thr->wait_list, address_get_mutex(h, DEPTH_THUNK), true);
3875 mutex_unlock(&socket_list_mutex);
3876 mem_free(select_arg);
3881 void iomux_init(void)
3885 void iomux_done(void)
3890 static void os2_init_tcpip(void)
3892 ajla_error_t sink;
3893 if (tcpip_loaded)
3894 return;
3896 hso32dll = os_dlopen("SO32DLL", &sink, NULL);
3897 if (!hso32dll)
3898 return;
3899 htcp32dll = os_dlopen("TCP32DLL", &sink, NULL);
3900 if (!htcp32dll)
3901 return;
3902 if (!os_dlsym(hso32dll, "ACCEPT", (void **)&proc_accept) ||
3903 !os_dlsym(hso32dll, "BIND", (void **)&proc_bind) ||
3904 !os_dlsym(hso32dll, "CONNECT", (void **)&proc_connect) ||
3905 !os_dlsym(htcp32dll, "GETHOSTBYNAME", (void **)&proc_gethostbyname) ||
3906 !os_dlsym(htcp32dll, "GETHOSTNAME", (void **)&proc_gethostname) ||
3907 !os_dlsym(hso32dll, "GETPEERNAME", (void **)&proc_getpeername) ||
3908 !os_dlsym(hso32dll, "GETSOCKNAME", (void **)&proc_getsockname) ||
3909 !os_dlsym(hso32dll, "GETSOCKOPT", (void **)&proc_getsockopt) ||
3910 !os_dlsym(hso32dll, "IOCTL", (void **)&proc_ioctl) ||
3911 !os_dlsym(hso32dll, "LISTEN", (void **)&proc_listen) ||
3912 !os_dlsym(hso32dll, "RECV", (void **)&proc_recv) ||
3913 !os_dlsym(hso32dll, "RECVFROM", (void **)&proc_recvfrom) ||
3914 !os_dlsym(hso32dll, "SELECT", (void **)&proc_select) ||
3915 !os_dlsym(hso32dll, "SEND", (void **)&proc_send) ||
3916 !os_dlsym(hso32dll, "SENDTO", (void **)&proc_sendto) ||
3917 !os_dlsym(hso32dll, "SETSOCKOPT", (void **)&proc_setsockopt) ||
3918 !os_dlsym(hso32dll, "SHUTDOWN", (void **)&proc_shutdown) ||
3919 !os_dlsym(hso32dll, "SOCKET", (void **)&proc_socket) ||
3920 !os_dlsym(hso32dll, "SOCK_ERRNO", (void **)&proc_sock_errno) ||
3921 !os_dlsym(hso32dll, "SOCK_INIT", (void **)&proc_sock_init) ||
3922 !os_dlsym(hso32dll, "SOCLOSE", (void **)&proc_soclose) ||
3923 !os_dlsym(htcp32dll, "H_ERRNO", (void **)&proc_h_errno) ||
3924 proc_sock_init()) {
3925 return;
3927 if (!os2_socketpair(os2_notify_socket))
3928 return;
3930 tcpip_loaded = true;
3933 bool os2_test_for_32bit_tcpip(const char *mem)
3935 int r;
3937 os2_init_tcpip();
3939 if (unlikely(!tcpip_loaded))
3940 return true;
3942 r = proc_send(os2_notify_socket[1], mem, 1, 0);
3944 return r >= 0;
3948 void os_init(void)
3950 APIRET rc;
3951 #ifdef DEBUG
3952 unsigned i;
3953 for (i = 0; i < n_array_elements(os2_error_to_system_error) - 1; i++)
3954 if (unlikely(os2_error_to_system_error[i].errn >= os2_error_to_system_error[i + 1].errn))
3955 internal(file_line, "os_init: os2_error_to_system_error is not monotonic at %u", i);
3956 for (i = 0; i < n_array_elements(socket_error_to_system_error) - 1; i++)
3957 if (unlikely(socket_error_to_system_error[i].errn >= socket_error_to_system_error[i + 1].errn))
3958 internal(file_line, "os_init: socket_error_to_system_error is not monotonic at %u", i);
3959 #endif
3960 os_threads_initialized = false;
3962 os_init_path_to_exe();
3964 n_std_handles = 0;
3965 while (1) {
3966 ULONG htype, hattr;
3967 rc = DosQueryHType(n_std_handles, &htype, &hattr);
3968 if (rc)
3969 break;
3970 n_std_handles++;
3973 if (unlikely(n_std_handles < 3))
3974 exit(127);
3976 tick_high = 0;
3977 tick_last = 0;
3979 tzset();
3981 hdoscalls = os_dlopen("DOSCALLS", NULL, NULL);
3983 os_dlsym(hdoscalls, "DosTmrQueryFreq", (void **)&proc_DosTmrQueryFreq);
3984 os_dlsym(hdoscalls, "DosTmrQueryTime", (void **)&proc_DosTmrQueryTime);
3985 if (proc_DosTmrQueryFreq) {
3986 ULONG tmr_freq;
3987 QWORD qw;
3988 rc = proc_DosTmrQueryFreq(&tmr_freq);
3989 if (unlikely(rc != 0))
3990 goto no_dos_tmr_q;
3991 freq_period_usec = (long double)1000000 / tmr_freq;
3992 rc = proc_DosTmrQueryTime(&qw);
3993 if (unlikely(rc != 0))
3994 goto no_dos_tmr_q;
3995 } else {
3996 no_dos_tmr_q:
3997 proc_DosTmrQueryFreq = NULL;
3998 proc_DosTmrQueryTime = NULL;
4000 os_dlsym(hdoscalls, "DosOpenL", (void **)&proc_DosOpenL);
4001 os_dlsym(hdoscalls, "DosSetFilePtrL", (void **)&proc_DosSetFilePtrL);
4002 os_dlsym(hdoscalls, "DosSetFileSizeL", (void **)&proc_DosSetFileSizeL);
4003 /*debug("%p %p %p", proc_DosOpenL, proc_DosSetFilePtrL, proc_DosSetFileSizeL);*/
4005 os2_init_tcpip();
4007 os_cwd = os_dir_cwd(NULL);
4010 void os_done(void)
4012 os_dir_close(os_cwd);
4014 if (tcpip_loaded) {
4015 os2_close_socket(os2_notify_socket[0]);
4016 os2_close_socket(os2_notify_socket[1]);
4017 tcpip_loaded = false;
4021 void os_init_multithreaded(void)
4023 unsigned u;
4024 APIRET rc;
4026 os_init_calendar_lock();
4028 mutex_init(&tick_mutex);
4029 list_init(&deferred_write_list);
4030 list_init(&deferred_closed_list);
4031 mutex_init(&deferred_mutex);
4032 os_threads_initialized = true;
4034 tree_init(&proc_tree);
4035 mutex_init(&proc_tree_mutex);
4036 proc_wait_thread = 0;
4038 os2_std_handles = mem_alloc_array_mayfail(mem_alloc_mayfail, handle_t *, 0, 0, n_std_handles, sizeof(handle_t), NULL);
4039 for (u = 0; u < n_std_handles; u++) {
4040 os2_set_inherit(u, false);
4041 os2_std_handles[u] = os2_hfile_to_handle(u, (!u ? O_RDONLY : O_WRONLY) | O_NONBLOCK, 0, NULL);
4044 #if 0
4046 int i = 0;
4047 while (1) {
4048 debug("X1: %d", i++);
4049 os2_create_read_thread(os2_std_handles[0], NULL);
4050 /*os2_terminate_io_thread(&os2_std_handles[0]->rd);*/
4051 os_free_handle(os2_std_handles[0], false);
4052 os2_std_handles[0] = os2_hfile_to_handle(0, O_RDONLY | O_NONBLOCK, 0, NULL);
4055 #endif
4056 #if 0
4058 int i = 0;
4059 while (1) {
4060 ssize_t r;
4061 char c;
4062 handle_t p[2];
4063 debug("X2: %d", i++);
4064 os_pipe(p, 3, NULL);
4065 r = os_read(p[0], &c, 1, NULL);
4066 r = r;
4067 r = rand() & 0xffff;
4068 while (r--)
4069 __asm__ volatile("nop":::"memory");
4070 os_close(p[0]);
4071 os_close(p[1]);
4074 #endif
4076 signal_thread_running = false;
4077 rc = DosCreateEventSem(NULL, &signal_ev, 0, FALSE);
4078 if (unlikely(rc != 0)) {
4079 fatal("DosCreateEventSem failed: %lu", rc);
4081 mutex_init(&signal_mutex);
4082 memset(signals, 0, sizeof signals);
4083 for (u = 0; u < N_SIG; u++)
4084 list_init(&signals[u].wait_list);
4086 if (tcpip_loaded) {
4087 mutex_init(&socket_list_mutex);
4088 list_init(&socket_list[0]);
4089 list_init(&socket_list[1]);
4090 thread_spawn(&iomux_thread, iomux_poll_thread, NULL, PRIORITY_IO, NULL);
4094 void os_done_multithreaded(void)
4096 APIRET rc;
4097 unsigned u;
4098 TID pwt;
4100 if (signal_thread_running) {
4101 signal_thread_running = false;
4102 rc = DosPostEventSem(signal_ev);
4103 if (unlikely(rc != 0) && unlikely(rc != ERROR_ALREADY_POSTED) && rc != ERROR_TOO_MANY_POSTS)
4104 internal(file_line, "DosPostEventSem returned an error: %lu", rc);
4105 thread_join(&signal_thread);
4107 rc = DosCloseEventSem(signal_ev);
4108 if (unlikely(rc != 0))
4109 internal(file_line, "DosCloseEventSem returned an error: %lu", rc);
4111 mutex_done(&signal_mutex);
4113 if (tcpip_loaded) {
4114 os2_shutdown_notify_pipe();
4115 thread_join(&iomux_thread);
4116 ajla_assert_lo(list_is_empty(&socket_list[0]), (file_line, "os_done_multithreaded: read socket list is not empty"));
4117 ajla_assert_lo(list_is_empty(&socket_list[1]), (file_line, "os_done_multithreaded: write socket list is not empty"));
4118 mutex_done(&socket_list_mutex);
4121 for (u = 0; u < n_std_handles; u++) {
4122 os_free_handle(os2_std_handles[u], false);
4123 os2_set_inherit(u, true);
4125 mem_free(os2_std_handles);
4126 os2_std_handles = NULL;
4128 mutex_lock(&deferred_mutex);
4129 while (!list_is_empty(&deferred_write_list)) {
4130 mutex_unlock(&deferred_mutex);
4131 DosSleep(1);
4132 mutex_lock(&deferred_mutex);
4134 mutex_unlock(&deferred_mutex);
4135 os2_clean_up_handles();
4137 proc_lock();
4138 if (unlikely(!tree_is_empty(&proc_tree))) {
4139 struct proc_handle *ph = get_struct(tree_any(&proc_tree), struct proc_handle, entry);
4140 tree_delete(&ph->entry);
4141 os2_free_buffer(ph);
4143 pwt = proc_wait_thread;
4144 proc_unlock();
4145 if (pwt)
4146 os2_terminate_thread(pwt, true);
4148 mutex_done(&proc_tree_mutex);
4149 mutex_done(&deferred_mutex);
4150 mutex_done(&tick_mutex);
4152 os_done_calendar_lock();
4154 os_threads_initialized = false;
4157 #endif