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
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/>.
35 #define MAXSOCKETS 2048
39 #include <sys/socket.h>
40 #include <sys/so_ioctl.h>
41 #include <netinet/in.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
);
56 struct os2_io_thread
{
66 bool packet_is_queued
;
67 unsigned char last_buttons
;
70 struct console_read_packet pkt
;
71 struct list wait_list
;
72 struct list socket_entry
;
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"
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
;
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
{
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
)
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)
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)
305 uint32_t os_get_last_socket_error(void)
307 if (unlikely(!tcpip_loaded
)) {
310 return proc_sock_errno();
314 void os_block_signals(sig_state_t attr_unused
*set
)
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
)
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)
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
)
371 static void *os2_alloc_buffer(size_t size
)
376 rc
= DosAllocMem(&addr
, size
, PAG_READ
| PAG_WRITE
| PAG_COMMIT
);
377 if (unlikely(rc
!= 0)) {
378 if (rc
== ERROR_INTERRUPT
)
385 static void os2_free_buffer(void *addr
)
389 rc
= DosFreeMem(addr
);
391 if (rc
== ERROR_INTERRUPT
)
393 internal(file_line
, "os2_free_buffer: DosFreeMem(%p) returned error: %lu", addr
, rc
);
397 static bool os2_increase_open_files(void)
400 rc
= DosSetMaxFH(OS2_MAX_HANDLE
);
404 static void os2_set_inherit(HFILE hfile
, bool inherit
)
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
;
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
)
426 rc
= DosQueryFHState(hfile
, &state
);
430 static bool os2_handle_dup1(HFILE hfile
, HFILE
*n
, ajla_error_t
*err
)
433 bool increased
= false;
436 rc
= DosDupHandle(hfile
, n
);
437 if (unlikely(rc
!= 0)) {
439 if (!increased
&& rc
== ERROR_TOO_MANY_OPEN_FILES
) {
440 if (os2_increase_open_files()) {
445 e
= error_from_os2(EC_SYSCALL
, rc
);
446 fatal_mayfail(e
, err
, "can't duplicate handle: %s", error_decode(e
));
452 static bool os2_handle_dup2(HFILE hfile
, HFILE n
, ajla_error_t
*err
)
456 bool increased
= false;
459 rc
= DosDupHandle(hfile
, &nn
);
460 if (unlikely(rc
!= 0)) {
462 if (!increased
&& (rc
== ERROR_TOO_MANY_OPEN_FILES
|| rc
== ERROR_INVALID_TARGET_HANDLE
)) {
463 if (os2_increase_open_files()) {
468 e
= error_from_os2(EC_SYSCALL
, rc
);
469 fatal_mayfail(e
, err
, "can't duplicate handle: %s", error_decode(e
));
475 static bool os2_handle_placeholder(HFILE h
, ajla_error_t
*err
)
480 bool increased
= false;
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)) {
485 if (!increased
&& rc
== ERROR_TOO_MANY_OPEN_FILES
) {
486 if (os2_increase_open_files()) {
491 e
= error_from_os2(EC_SYSCALL
, rc
);
492 fatal_mayfail(e
, err
, "can't open device 'con': %s", error_decode(e
));
497 if (unlikely(!os2_handle_dup2(hfile
, h
, err
))) {
498 os2_close_handle(hfile
);
501 os2_close_handle(hfile
);
505 static handle_t
os2_hfile_to_handle(HFILE hfile
, int flags
, char disk
, ajla_error_t
*err
)
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
);
517 os2_close_handle(hfile
);
524 if (h
->t
== HANDTYPE_FILE
)
525 flags
&= ~O_NONBLOCK
;
527 list_init(&h
->rd
.wait_list
);
528 list_init(&h
->wr
.wait_list
);
533 obj_registry_insert(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
538 static handle_t
os2_socket_to_handle(int sock
, ajla_error_t
*err
)
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
);
549 h
= mem_calloc_mayfail(handle_t
, sizeof(struct os2_handle
), err
);
551 os2_close_socket(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
);
563 obj_registry_insert(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
568 uintptr_t os_handle_to_number(handle_t h
)
573 handle_t
os_number_to_handle(uintptr_t n
, bool sckt
, ajla_error_t
*err
)
576 return os2_hfile_to_handle(n
, O_RDWR
, 0, err
);
578 if (unlikely(!tcpip_loaded
)) {
579 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not installed");
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
)
591 ULONG open_flag
, attrs
, action
;
597 os2_clean_up_handles();
599 joined
= os_join_paths(dir
, path
, false, err
);
600 if (unlikely(!joined
))
604 if (flags
& O_CREAT
) {
605 open_flag
|= OPEN_ACTION_CREATE_IF_NEW
;
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
;
614 open_flag
|= OPEN_ACTION_OPEN_IF_EXISTS
;
619 attrs
|= FILE_READONLY
;
623 if (likely(proc_DosOpenL
!= NULL
)) {
624 /* * DosOpenL crashes if the address is in high memory */
625 size_t l_joined
= strlen(joined
) + 1;
627 if (l_joined
>= 512) {
628 fn
= os2_alloc_buffer(l_joined
);
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
);
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) {
643 rc
= DosOpen(joined
, &hfile
, &action
, 0, attrs
, open_flag
, (flags
& 0x3) | OPEN_SHARE_DENYNONE
| OPEN_FLAGS_NOINHERIT
, NULL
);
646 if (unlikely(rc
!= 0)) {
648 if (!increased
&& rc
== ERROR_TOO_MANY_OPEN_FILES
) {
649 if (os2_increase_open_files()) {
654 e
= error_from_os2(EC_SYSCALL
, rc
);
655 fatal_mayfail(e
, err
, "can't open file '%s': %s", joined
, error_decode(e
));
660 h
= os2_hfile_to_handle(hfile
, flags
, joined
[0], err
);
665 bool os_pipe(handle_t result
[2], int nonblock_flags
, ajla_error_t
*err
)
671 os2_clean_up_handles();
675 os2_enter_critical_section();
677 rc
= DosCreatePipe(&h1
, &h2
, OS2_PIPE_SIZE
);
678 if (unlikely(rc
!= 0)) {
680 os2_exit_critical_section();
681 if (!increased
&& rc
== ERROR_TOO_MANY_OPEN_FILES
) {
682 if (os2_increase_open_files()) {
687 e
= error_from_os2(EC_SYSCALL
, rc
);
688 fatal_mayfail(e
, err
, "can't create pipe: %s", error_decode(e
));
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
);
702 result
[1] = os2_hfile_to_handle(h2
, O_WRONLY
| (nonblock_flags
& 2 ? O_NONBLOCK
: 0), 0, err
);
703 if (unlikely(!result
[1])) {
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
);
720 os2_terminate_io_thread(&h
->rd
);
723 os2_terminate_io_thread(&h
->ms
);
726 address_lock(h
, DEPTH_THUNK
);
727 if (h
->wr
.buffer_len
!= 0 && !h
->wr
.err
) {
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
);
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
);
749 os2_close_handle(h
->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
)
789 A_DECL(KBDKEYINFO
, kbki
);
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))
802 if (!kbki
->chChar
&& !kbki
->chScan
) {
806 /*debug("key: %u, vkey: %u, fsstate: %x", kbki->chChar, kbki->chScan, kbki->fsState);*/
807 memset(&rd
->pkt
, 0, sizeof(struct console_read_packet
));
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))
821 static APIRET
os2_read_mouse_packet(struct os2_io_thread
*rd
)
825 A_DECL(MOUEVENTINFO
, mei
);
826 rc
= MouReadEventQue(mei
, &w
, rd
->mh
);
827 if (unlikely(rc
!= 0))
829 memset(&rd
->pkt
, 0, sizeof(struct console_read_packet
));
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
;
847 static void os2_read_thread(ULONG rd_
)
849 struct os2_io_thread
*rd
= num_to_ptr(rd_
);
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
);
861 } else if (rd
->packet_mode
) {
862 if (rd
->packet_is_queued
)
865 address_unlock(h
, DEPTH_THUNK
);
866 os_unblock_signals(&s
);
869 rc
= os2_read_keyboard_packet(rd
);
870 else if (rd
== &h
->ms
)
871 rc
= os2_read_mouse_packet(rd
);
873 internal(file_line
, "os2_read_thread: invalid pointer %p, %p", rd
, h
);
875 os_block_signals(&s
);
876 address_lock(h
, DEPTH_THUNK
);
879 rd
->packet_is_queued
= true;
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
;
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)) {
899 if (unlikely(!rd_num
)) {
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
);
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
);
914 rc
= DosWaitEventSem(rd
->event
, SEM_INDEFINITE_WAIT
);
915 if (unlikely(rc
!= 0)) {
916 if (rc
== ERROR_INTERRUPT
|| rc
== ERROR_TIMEOUT
)
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_
);
931 os_block_signals(&s
);
932 address_lock(h
, DEPTH_THUNK
);
933 if (unlikely(wr
->err
)) {
936 address_unlock(h
, DEPTH_THUNK
);
937 os_unblock_signals(&s
);
940 } else if (wr
->buffer_len
) {
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
);
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)) {
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
)) {
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
);
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
);
984 rc
= DosWaitEventSem(wr
->event
, SEM_INDEFINITE_WAIT
);
985 if (unlikely(rc
!= 0)) {
986 if (rc
== ERROR_INTERRUPT
|| rc
== ERROR_TIMEOUT
)
988 internal(file_line
, "DosWaitEventSem returned an error: %lu", rc
);
994 static void os2_clear_thread_buffer(struct os2_io_thread
*thr
)
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
)
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
);
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
));
1023 if (thr
== &h
->ms
) {
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
);
1036 ajla_error_t e
= error_from_os2(EC_SYSCALL
, rc
);
1037 fatal_mayfail(e
, err
, "MouOpen returned an error: %s", error_decode(e
));
1042 rc
= MouSetEventMask(&evmask
, mh
);
1044 ajla_error_t e
= error_from_os2(EC_SYSCALL
, rc
);
1045 fatal_mayfail(e
, err
, "MouSetEventMask returned an error: %s", error_decode(e
));
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
));
1065 rc
= DosCloseEventSem(thr
->event
);
1066 if (unlikely(rc
!= 0))
1067 internal(file_line
, "DosCloseEventSem returned an error: %lu", rc
);
1070 os2_free_buffer(thr
->buffer
);
1077 static void os2_terminate_thread(TID t
, bool accept_invalid
)
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
))
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
)
1100 os2_terminate_thread(thr
->thread
, false);
1105 os2_free_buffer(thr
->buffer
);
1109 rc
= DosCloseEventSem(thr
->event
);
1110 if (unlikely(rc
!= 0))
1111 internal(file_line
, "DosCloseEventSem returned an error: %lu", rc
);
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
);
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
);
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
);
1141 static void os2_terminate_read_threads(handle_t h
)
1144 os2_terminate_io_thread(&h
->rd
);
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
)
1156 address_lock(h
, DEPTH_THUNK
);
1157 if (unlikely(h
->packet_mode
)) {
1158 h
->packet_mode
= false;
1159 os2_terminate_read_threads(h
);
1162 if (unlikely(!os2_create_read_thread(h
, err
))) {
1163 address_unlock(h
, DEPTH_THUNK
);
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
;
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
);
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
;
1185 if (unlikely(h
->rd
.eof
)) {
1189 this_len
= OS_RW_WOULDBLOCK
;
1192 address_unlock(h
, DEPTH_THUNK
);
1196 static ssize_t
os_write_nonblock(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
1200 address_lock(h
, DEPTH_THUNK
);
1201 if (unlikely(!os2_create_write_thread(h
, err
))) {
1202 address_unlock(h
, DEPTH_THUNK
);
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
;
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
;
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
);
1224 this_len
= OS_RW_WOULDBLOCK
;
1227 address_unlock(h
, DEPTH_THUNK
);
1231 static bool os2_setfilesize(handle_t h
, os_off_t size
, ajla_error_t
*err
)
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");
1238 if (likely(proc_DosSetFileSizeL
!= NULL
)) {
1239 rc
= proc_DosSetFileSizeL(h
->h
, size
);
1241 if (unlikely(size
!= (LONG
)size
)) {
1242 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_SIZE_OVERFLOW
), err
, "file size overflow");
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
));
1255 static bool os2_setfileptr(handle_t h
, os_off_t off
, ULONG rel
, os_off_t
*result
, ajla_error_t
*err
)
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");
1263 if (likely(proc_DosSetFilePtrL
!= NULL
)) {
1264 rc
= proc_DosSetFilePtrL(h
->h
, off
, rel
, result
);
1266 if (unlikely(off
!= (LONG
)off
)) {
1267 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_SIZE_OVERFLOW
), err
, "file size overflow");
1270 rc
= DosSetFilePtr(h
->h
, off
, rel
, &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
));
1281 ssize_t
os_do_rw(handle_t h
, char *buffer
, int size
, bool wr
, ajla_error_t
*err
)
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
);
1294 memcpy(bb
, buffer
, size
);
1297 rc
= DosRead(h
->h
, bounce
? bb
: buffer
, size
, &result
);
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
));
1313 static ssize_t
os_read_socket(handle_t h
, char *buffer
, int size
, ajla_error_t
*err
)
1317 r
= proc_recv(h
->h
, buffer
, size
, 0);
1318 if (unlikely(r
== -1)) {
1319 int er
= proc_sock_errno();
1322 if (er
== SOCEAGAIN
)
1323 return OS_RW_WOULDBLOCK
;
1324 fatal_mayfail(error_from_os2_socket(), err
, "error reading socket");
1330 static ssize_t
os_write_socket(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
1334 r
= proc_send(h
->h
, buffer
, size
, 0);
1335 if (unlikely(r
== -1)) {
1336 int er
= proc_sock_errno();
1339 if (er
== SOCEAGAIN
)
1340 return OS_RW_WOULDBLOCK
;
1341 fatal_mayfail(error_from_os2_socket(), err
, "error writing socket");
1347 ssize_t
os_read(handle_t h
, char *buffer
, int size
, ajla_error_t
*err
)
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");
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
);
1366 ssize_t
os_write(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
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");
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
) {
1381 if (unlikely(!os2_setfileptr(h
, 0, FILE_END
, &sink
, err
))) {
1386 res
= os_do_rw(h
, cast_ptr(char *, buffer
), size
, true, err
);
1388 if (likely(os_threads_initialized
) && h
->flags
& O_APPEND
&& h
->t
== HANDTYPE_FILE
)
1389 address_unlock(h
, DEPTH_THUNK
);
1393 ssize_t
os_pread(handle_t h
, char *buffer
, int size
, os_off_t off
, ajla_error_t
*err
)
1397 if (unlikely(h
->t
== HANDTYPE_SOCKET
)) {
1398 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "seek operation on socket");
1401 if (likely(os_threads_initialized
))
1402 address_lock(h
, DEPTH_THUNK
);
1403 if (unlikely(!os2_setfileptr(h
, off
, FILE_BEGIN
, &sink
, err
))) {
1407 res
= os_do_rw(h
, buffer
, size
, false, err
);
1409 if (likely(os_threads_initialized
))
1410 address_unlock(h
, DEPTH_THUNK
);
1414 ssize_t
os_pwrite(handle_t h
, const char *buffer
, int size
, os_off_t off
, ajla_error_t
*err
)
1418 if (unlikely(h
->t
== HANDTYPE_SOCKET
)) {
1419 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "seek operation on socket");
1422 if (likely(os_threads_initialized
))
1423 address_lock(h
, DEPTH_THUNK
);
1424 if (unlikely(!os2_setfileptr(h
, off
, FILE_BEGIN
, &sink
, err
))) {
1428 res
= os_do_rw(h
, cast_ptr(char *, buffer
), size
, true, err
);
1430 if (likely(os_threads_initialized
))
1431 address_unlock(h
, DEPTH_THUNK
);
1435 bool os_lseek(handle_t h
, unsigned mode
, os_off_t off
, os_off_t
*result
, ajla_error_t
*err
)
1441 if (unlikely(h
->t
== HANDTYPE_SOCKET
)) {
1442 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "seek operation on socket");
1446 if (likely(os_threads_initialized
))
1447 address_lock(h
, DEPTH_THUNK
);
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
);
1456 if (unlikely(off
> len
))
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
);
1469 if (likely(os_threads_initialized
))
1470 address_unlock(h
, DEPTH_THUNK
);
1475 bool os_ftruncate(handle_t h
, os_off_t size
, ajla_error_t
*err
)
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");
1485 if (likely(os_threads_initialized
))
1486 address_lock(h
, DEPTH_THUNK
);
1488 ret
= os2_setfileptr(h
, 0, FILE_END
, ¤t_size
, err
);
1490 if (size
< current_size
) {
1491 ret
= os2_setfilesize(h
, size
, err
);
1492 } else if (size
> current_size
) {
1494 ret
= os2_setfileptr(h
, size
- 1, FILE_BEGIN
, &sink
, err
);
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
));
1509 if (likely(os_threads_initialized
))
1510 address_unlock(h
, DEPTH_THUNK
);
1515 bool os_fallocate(handle_t h
, os_off_t position
, os_off_t size
, ajla_error_t
*err
)
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");
1525 if (likely(os_threads_initialized
))
1526 address_lock(h
, DEPTH_THUNK
);
1528 ret
= os2_setfileptr(h
, 0, FILE_END
, ¤t_size
, err
);
1532 if (position
<= current_size
&& position
+ size
> current_size
) {
1533 ret
= os2_setfilesize(h
, position
+ size
, err
);
1539 if (likely(os_threads_initialized
))
1540 address_unlock(h
, DEPTH_THUNK
);
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");
1551 bool os_fsync(handle_t h
, unsigned mode
, ajla_error_t
*err
)
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");
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)
1567 if (mode
== 0 || mode
== 1)
1568 rc
= DosResetBuffer(h
->h
);
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
));
1580 int os_charset(void)
1584 rc
= VioGetCp(0, &cp
, 0);
1585 if (unlikely(rc
!= 0))
1590 ssize_t
os_read_console_packet(handle_t h
, struct console_read_packet
*result
, ajla_error_t
*err
)
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
;
1602 if (unlikely(!h
->packet_mode
)) {
1603 h
->packet_mode
= true;
1604 os2_terminate_read_threads(h
);
1607 if (unlikely(!os2_create_read_thread(h
, err
))) {
1608 retval
= OS_RW_ERROR
;
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
);
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
);
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
;
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
;
1642 retval
= OS_RW_WOULDBLOCK
;
1644 address_unlock(h
, DEPTH_THUNK
);
1648 bool os_write_console_packet(handle_t h
, struct console_write_packet
*packet
, ajla_error_t
*err
)
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");
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");
1663 switch (packet
->type
) {
1673 n_chars
= packet
->u
.c
.n_chars
;
1674 ptr
= packet
->u
.c
.data
;
1678 for (n
= 1; n
< n_chars
&& n
< OS2_PACKET_BUFFER_SIZE
; n
++) {
1679 if (ptr
[n
* 2 + 1] != ptr
[1])
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
));
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
));
1705 packet
= cast_ptr(struct console_write_packet
*, &packet
->u
.c
.data
[packet
->u
.c
.n_chars
* 2]);
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
));
1715 packet
= cast_ptr(struct console_write_packet
*, &packet
->u
.p
.end
);
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
));
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
));
1733 packet
= cast_ptr(struct console_write_packet
*, &packet
->u
.v
.end
);
1737 internal(file_line
, "os_write_console_packet: invalid type %d", (int)packet
->type
);
1741 os2_free_buffer(pch
);
1745 os2_free_buffer(pch
);
1751 dir_handle_t
os_dir_root(ajla_error_t
*err
)
1756 char *d
= str_dup(" :\\", -1, err
);
1759 rc
= DosQueryCurrentDisk(¤t
, &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
));
1775 dir_handle_t
os_dir_cwd(ajla_error_t
*err
)
1777 ULONG disk
, logical
;
1785 if (unlikely(!array_init_mayfail(char, &ptr
, &len
, err
)))
1788 rc
= DosQueryCurrentDisk(&disk
, &logical
);
1790 ajla_error_t e
= error_from_os2(EC_SYSCALL
, rc
);
1791 fatal_mayfail(e
, err
, "DosQueryCurrentDisk failed: %s", error_decode(e
));
1796 if (unlikely(!array_add_mayfail(char, &ptr
, &len
, disk
+ 'A' - 1, NULL
, err
)))
1798 if (unlikely(!array_add_multiple_mayfail(char, &ptr
, &len
, ":\\", 2, NULL
, err
)))
1803 buffer
= mem_alloc_mayfail(char *, buffer_len
, err
);
1804 if (unlikely(!buffer
)) {
1808 rc
= DosQueryCurrentDir(disk
, buffer
, &buffer_len
);
1809 if (rc
== ERROR_BUFFER_OVERFLOW
) {
1814 ajla_error_t e
= error_from_os2(EC_SYSCALL
, rc
);
1815 fatal_mayfail(e
, err
, "DosQueryCurrentDir failed: %s", error_decode(e
));
1820 if (unlikely(!array_add_multiple_mayfail(char, &ptr
, &len
, buffer
, strlen(buffer
) + 1, NULL
, err
))) {
1825 array_finish(char, &ptr
, &len
);
1827 while ((p
= strchr(p
, '\\')))
1832 static bool os2_dir_set(dir_handle_t dir
, ajla_error_t
*err
)
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
));
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
));
1853 dir_handle_t
os_dir_open(dir_handle_t dir
, const char *path
, int attr_unused flags
, ajla_error_t
*err
)
1858 result
= os_join_paths(dir
, path
, true, err
);
1859 if (unlikely(!result
))
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
));
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
));
1877 void os_dir_close(dir_handle_t 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
--) {
1900 FILEFINDBUF3
*ffb
= cast_ptr(FILEFINDBUF3
*, buffer
);
1901 char *name
= ffb
->achName
;
1902 buffer
+= ffb
->oNextEntryOffset
;
1903 if (unlikely(!strcmp(name
, ".")) || unlikely(!strcmp(name
, "..")))
1905 name
= str_dup(name
, -1, err
);
1906 if (unlikely(!name
))
1908 if (unlikely(!array_add_mayfail(char *, files
, n_files
, name
, &err_ptr
, err
))) {
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
];
1924 if (unlikely(!array_init_mayfail(char *, files
, n_files
, err
)))
1927 fn
= os_join_paths(h
, "*", false, err
);
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
);
1934 if (unlikely(rc
!= 0)) {
1936 if (likely(rc
== ERROR_NO_MORE_FILES
))
1938 e
= error_from_os2(EC_SYSCALL
, rc
);
1939 fatal_mayfail(e
, err
, "error reading directory '%s': %s", h
, error_decode(e
));
1943 if (unlikely(!process_find_buffer(find_buffer
, n_entries
, files
, n_files
, err
))) {
1947 n_entries
= FIND_BUFFER_SIZE
;
1948 rc
= DosFindNext(hdir
, find_buffer
, FIND_BUFFER_SIZE
, &n_entries
);
1949 if (likely(rc
!= 0)) {
1952 if (likely(rc
== ERROR_NO_MORE_FILES
))
1954 e
= error_from_os2(EC_SYSCALL
, rc
);
1955 fatal_mayfail(e
, err
, "error reading directory '%s': %s", h
, error_decode(e
));
1964 os_dir_free(*files
, *n_files
);
1968 void os_dir_free(char **files
, size_t n_files
)
1971 for (i
= 0; i
< n_files
; i
++)
1977 unsigned os_dev_t_major(dev_t attr_unused dev
)
1982 unsigned os_dev_t_minor(dev_t attr_unused dev
)
1987 static os_time_t
time_to_os_time(int year
, int month
, int day
, int hour
, int min
, int sec
)
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;
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
;
2007 static void os_time_to_time(os_time_t t
, int *year
, int *month
, int *day
, int *hour
, int *min
, int *sec
)
2011 if (os_threads_initialized
)
2012 address_lock(NULL
, DEPTH_THUNK
);
2014 if (unlikely(!tm
)) {
2015 *year
= *month
= *day
= *hour
= *min
= *sec
= 0;
2017 *year
= tm
->tm_year
+ 1900;
2018 *month
= tm
->tm_mon
;
2020 *hour
= tm
->tm_hour
;
2024 if (os_threads_initialized
)
2025 address_unlock(NULL
, DEPTH_THUNK
);
2029 time_t _mktime(struct tm
*);
2030 static os_time_t
file_time_to_os_time(FDATE
*fd
, FTIME
*ft
)
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
);
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;
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
;
2060 os_time_to_time(t
, &year
, &month
, &day
, &hour
, &min
, &sec
);
2061 fd
->year
= year
- 1980;
2066 ft
->twosecs
= sec
/ 2;
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
)
2086 memset(st
, 0, sizeof(os_stat_t
));
2090 case HANDTYPE_SOCKET
:
2091 st
->st_mode
= S_IFSOCK
| 0600;
2093 case HANDTYPE_DEVICE
:
2094 st
->st_mode
= S_IFCHR
;
2097 st
->st_mode
= S_IFIFO
;
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;
2105 st
->st_mode
|= 0666;
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
));
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;
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;
2131 bool os_stat(dir_handle_t dir
, const char *path
, bool attr_unused lnk
, os_stat_t
*st
, ajla_error_t
*err
)
2138 memset(st
, 0, sizeof(os_stat_t
));
2141 dh
= os_dir_open(dir
, path
, 0, &sink
);
2142 if (dir_handle_is_valid(dh
)) {
2143 st
->st_mode
= S_IFDIR
| 0777;
2145 st
->st_mode
= S_IFREG
| 0666;
2146 dh
= os_join_paths(dir
, path
, false, err
);
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
));
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;
2169 static bool os_stat_disk(char disk
, os_statvfs_t
*st
, ajla_error_t
*err
)
2175 if (unlikely(disk
< 'A') || unlikely(disk
> 'Z')) {
2176 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "statvfs not supported");
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
));
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;
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");
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
)
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
))
2223 rc
= DosDelete(joined
);
2225 case IO_Action_Rm_Dir
:
2226 rc
= DosDeleteDir(joined
);
2228 case IO_Action_Mk_Dir
:
2229 rc
= DosCreateDir(joined
, NULL
);
2230 if (rc
== ERROR_ACCESS_DENIED
) {
2232 rc2
= DosQueryPathInfo(joined
, FIL_STANDARD
, &fs
, sizeof fs
);
2234 rc
= ERROR_FILE_EXISTS
;
2237 case IO_Action_Mk_Pipe
:
2238 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mkpipe not supported");
2240 case IO_Action_Mk_Socket
:
2241 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mksocket not supported");
2243 case IO_Action_Mk_CharDev
:
2244 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mkchardev not supported");
2246 case IO_Action_Mk_BlockDev
:
2247 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mkblockdev not supported");
2249 case IO_Action_Mk_SymLink
:
2250 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mksymlink not supported");
2252 case IO_Action_ChMod
:
2253 case IO_Action_ChOwn
:
2254 case IO_Action_LChOwn
:
2255 rc
= DosQueryPathInfo(joined
, FIL_STANDARD
, &fs
, sizeof fs
);
2257 case IO_Action_UTime
:
2258 case IO_Action_LUTime
: {
2259 rc
= DosQueryPathInfo(joined
, FIL_STANDARD
, &fs
, sizeof fs
);
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);
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
));
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
)
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
))
2293 src_joined
= os_join_paths(src_dir
, src_path
, false, err
);
2294 if (unlikely(!src_joined
))
2298 case IO_Action_Mk_Link
:
2299 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mklink not supported");
2301 case IO_Action_Rename
:
2302 rc
= DosQueryPathInfo(src_joined
, FIL_STANDARD
, &fs
, sizeof fs
);
2304 DosDelete(dest_joined
);
2305 rc
= DosMove(src_joined
, dest_joined
);
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
));
2318 mem_free(dest_joined
);
2319 mem_free(src_joined
);
2324 mem_free(dest_joined
);
2326 mem_free(src_joined
);
2330 bool os_drives(char **drives
, size_t *drives_l
, ajla_error_t
*err
)
2334 /* copied in os_win32.c:os_drives */
2336 rc
= DosQueryCurrentDisk(&c
, &mask
);
2339 if (rc
== ERROR_INTERRUPT
)
2341 e
= error_from_os2(EC_SYSCALL
, rc
);
2342 fatal_mayfail(e
, err
, "can't query current disk: %s", error_decode(e
));
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)
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
));
2373 for (i
= 0; os_path_to_exe
[i
]; i
++)
2374 if (os_is_path_separator(os_path_to_exe
[i
]))
2376 os_path_to_exe
[j
] = 0;
2380 bool os_tcgetattr(handle_t attr_unused h
, os_termios_t
*t
, ajla_error_t
*err
)
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
));
2391 if (kbi
->fsMask
& 2)
2392 t
->tc_flags
|= IO_Stty_Flag_Noecho
;
2396 bool os_tcsetattr(handle_t attr_unused h
, const os_termios_t
*t
, ajla_error_t
*err
)
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
));
2407 if (t
->tc_flags
& IO_Stty_Flag_Noecho
)
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
));
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
);
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
));
2446 const char *os_get_flavor(void)
2451 void os_get_uname(os_utsname_t
*un
)
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) {
2462 if (version
[1] == 10) {
2464 } else if (version
[1] >= 30) {
2465 version
[0] = version
[1] / 10;
2469 sprintf(un
->release
, "%d.%d", (int)version
[0], (int)version
[1]);
2473 strcpy(un
->machine
, ARCH_NAME
);
2477 char *os_get_host_name(ajla_error_t
*err
)
2481 char nodename
[256] = "";
2482 proc_gethostname(nodename
, sizeof nodename
- 1);
2484 return str_dup(nodename
, -1, err
);
2486 e
= getenv("HOSTNAME");
2489 return str_dup(e
, -1, err
);
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)
2502 EINTR_LOOP(r
, gettimeofday(&tv
, NULL
));
2503 if (unlikely(r
== -1)) {
2505 fatal("gettimeofday failed: %d, %s", e
, error_decode(error_from_errno(EC_SYSCALL
, e
)));
2507 return os_timeval_to_ajla_time(&tv
);
2510 ajla_time_t
os_time_real(void)
2514 int year
, month
, day
, hour
, min
, sec
;
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
));
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;
2532 static mutex_t tick_mutex
;
2533 static ULONG tick_last
;
2534 static ULONG tick_high
;
2536 ajla_time_t
os_time_monotonic(void)
2542 if (likely(proc_DosTmrQueryTime
!= NULL
)) {
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
);
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
))
2565 ret
= ((ajla_time_t
)tick_high
* (1 << 31) * 2) + t
;
2566 if (likely(os_threads_initialized
))
2567 mutex_unlock(&tick_mutex
);
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
);
2597 if (unlikely(h
->rd
.buffer_len
!= 0) || unlikely(h
->rd
.packet_is_queued
) || unlikely(h
->rd
.err
!= 0) || unlikely(h
->rd
.eof
))
2599 if (unlikely(h
->ms
.buffer_len
!= 0) || unlikely(h
->ms
.packet_is_queued
) || unlikely(h
->ms
.err
!= 0) || unlikely(h
->ms
.eof
))
2602 if (unlikely(h
->wr
.buffer_len
!= OS2_BUFFER_SIZE
) || unlikely(h
->wr
.err
!= 0))
2605 address_unlock(h
, DEPTH_THUNK
);
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
) {
2619 r
= proc_select(sel
, !wr
, wr
, 0, 0);
2620 if (unlikely(r
== -1)) {
2621 int er
= proc_sock_errno();
2624 internal(file_line
, "select returned an error: %d", er
);
2629 * os_read/os_write is non-blocking even for standard handles,
2630 * so we don't need this function
2636 struct proc_handle
{
2637 struct tree_entry entry
;
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;
2667 static bool proc_addstr(char **ptr
, size_t *len
, const char *str
, bool cvt_slashes
, ajla_error_t
*err
)
2672 if ((*ptr
)[*len
- 1]) {
2673 if (unlikely(!array_add_mayfail(char, ptr
, len
, ' ', NULL
, err
)))
2676 if (!str
[0] || str
[strcspn(str
, " \t")])
2680 if (unlikely(!array_add_mayfail(char, ptr
, len
, '"', NULL
, err
)))
2684 for (i
= 0; str
[i
]; i
++) {
2686 if (cvt_slashes
&& c
== '/')
2690 } else if (c
== '"') {
2691 for (j
= 0; j
<= bs
; j
++)
2692 if (unlikely(!array_add_mayfail(char, ptr
, len
, '\\', NULL
, err
)))
2698 if (unlikely(!array_add_mayfail(char, ptr
, len
, c
, NULL
, err
)))
2702 for (j
= 0; j
< bs
; j
++)
2703 if (unlikely(!array_add_mayfail(char, ptr
, len
, '\\', NULL
, err
)))
2705 if (unlikely(!array_add_mayfail(char, ptr
, len
, '"', NULL
, err
)))
2711 static void os2_wait_thread(ULONG attr_unused x
)
2715 os_block_signals(&s
);
2719 while (!tree_is_empty(&proc_tree
)) {
2723 struct tree_entry
*e
;
2724 struct proc_handle
*ph
;
2727 os_unblock_signals(&s
);
2729 rc
= DosWaitChild(DCWA_PROCESS
, DCWW_WAIT
, &codes
, &pid
, 0);
2731 internal(file_line
, "DosWaitChild returned an error: %lu", rc
);
2733 os_block_signals(&s
);
2736 e
= tree_find(&proc_tree
, proc_handle_compare
, pid
);
2741 ph
= get_struct(e
, struct proc_handle
, entry
);
2744 tree_delete(&ph
->entry
);
2746 if (!ph
->detached
) {
2747 call(wake_up_wait_list
)(&ph
->wait_list
, &proc_tree_mutex
, false);
2750 os2_free_buffer(ph
);
2753 proc_wait_thread
= 0;
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
)
2762 char *ptr
, *copy_of_ptr
, *copy_of_env
;
2763 size_t len
, env_len
;
2764 char obj_name_buf
[260];
2768 ajla_error_t err_x
, err_xx
;
2769 struct proc_handle
*ph
;
2770 struct tree_entry
*en
;
2771 struct tree_insert_position ins
;
2773 short mapping_table
[OS2_MAX_HANDLE
];
2776 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "empty arguments in spawn");
2780 if (unlikely(!array_init_mayfail(char, &ptr
, &len
, err
)))
2783 for (a
= args
; *a
; a
++) {
2784 if (!proc_addstr(&ptr
, &len
, *a
, a
== args
, err
))
2787 if (unlikely(!array_add_mayfail(char, &ptr
, &len
, 0, NULL
, err
)))
2791 if (unlikely(!array_add_mayfail(char, &ptr
, &len
, 0, NULL
, err
)))
2793 if (unlikely(!array_add_mayfail(char, &ptr
, &len
, 0, NULL
, err
)))
2796 if (unlikely(len
>= 65536)) {
2798 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_SIZE_OVERFLOW
), err
, "arguments size overflow");
2803 * The EMX source code says that the argument buffer must not cross
2806 copy_of_ptr
= os2_alloc_buffer(len
);
2807 if (unlikely(!copy_of_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
);
2812 memcpy(copy_of_ptr
, ptr
, len
);
2815 for (env_len
= 0; envc
[env_len
];)
2816 env_len
+= strlen(envc
+ env_len
) + 1;
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
);
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
);
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
);
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
));
2855 os2_free_buffer(copy_of_ptr
);
2856 os2_free_buffer(copy_of_env
);
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
];
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");
2873 address_lock(s
, DEPTH_THUNK
);
2875 os2_terminate_io_thread(&s
->rd
);
2878 os2_terminate_io_thread(&s
->ms
);
2880 address_unlock(s
, DEPTH_THUNK
);
2882 if (tgt
>= OS2_MAX_HANDLE
) {
2883 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_SIZE_OVERFLOW
), &err_x
, "destination handle too large");
2886 if (!os2_handle_is_valid(tgt
)) {
2887 /*debug("placeholder");*/
2888 if (unlikely(!os2_handle_placeholder(tgt
, &err_x
)))
2890 mapping_table
[tgt
] = -2;
2893 for (i
= 0; i
< n_handles
; i
++) {
2894 HFILE tgt
= target
[i
];
2895 if (mapping_table
[tgt
] == -1) {
2897 if (unlikely(!os2_handle_dup1(tgt
, &n
, &err_x
)))
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
)))
2911 os2_set_inherit(tgt
, true);
2914 if (unlikely(!os2_dir_set(wd
, err
))) {
2915 os2_exit_critical_section();
2918 os2_free_buffer(copy_of_ptr
);
2919 os2_free_buffer(copy_of_env
);
2923 rc
= DosExecPgm(obj_name_buf
, sizeof obj_name_buf
, EXEC_ASYNCRESULT
, copy_of_ptr
, copy_of_env
, &codes
, path
);
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();
2951 os2_free_buffer(copy_of_ptr
);
2952 os2_free_buffer(copy_of_env
);
2954 if (unlikely(rc
!= 0)) {
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
));
2962 fatal_mayfail(e
, err
, "error preparing handles for spawn: %s", error_decode(e
));
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
);
2981 void os_proc_free_handle(struct proc_handle
*ph
)
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"));
2987 os2_free_buffer(ph
);
2989 ph
->detached
= true;
2994 bool os_proc_register_wait(struct proc_handle
*ph
, mutex_t
**mutex_to_lock
, struct list
*list_entry
, int *status
)
2998 /*debug("exit: %lu, %lu", ph->codes.codeTerminate, ph->codes.codeResult);*/
2999 *status
= ph
->codes
.codeResult
;
3003 *mutex_to_lock
= &proc_tree_mutex
;
3004 list_add(&ph
->wait_list
, list_entry
);
3013 static mutex_t signal_mutex
;
3015 static bool signal_thread_running
;
3016 static thread_t signal_thread
;
3017 static HEV signal_ev
;
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
;
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];
3032 if (signals
[sig
].refcount
) {
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
)
3051 rc
= DosResetEventSem(signal_ev
, &count
);
3052 if (rc
&& unlikely(rc
!= ERROR_ALREADY_RESET
))
3053 internal(file_line
, "DosResetEventSem returned an error: %lu", rc
);
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);
3066 mutex_unlock(&signal_mutex
);
3067 if (unlikely(!signal_thread_running
))
3069 rc
= DosWaitEventSem(signal_ev
, SEM_INDEFINITE_WAIT
);
3070 if (unlikely(rc
!= 0)) {
3071 if (rc
== ERROR_INTERRUPT
|| rc
== ERROR_TIMEOUT
)
3073 internal(file_line
, "DosWaitEventSem returned an error: %lu", rc
);
3078 int os_signal_handle(const char *str
, signal_seq_t
*seq
, ajla_error_t
*err
)
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
;
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
);
3103 mutex_unlock(&signal_mutex
);
3107 void os_signal_unhandle(int sig
)
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
)
3122 mutex_lock(&signal_mutex
);
3123 seq
= signals
[sig
].last_sig_sequence
;
3124 mutex_unlock(&signal_mutex
);
3128 bool os_signal_wait(int sig
, signal_seq_t seq
, mutex_t
**mutex_to_lock
, struct list
*list_entry
)
3131 iomux_never(mutex_to_lock
, list_entry
);
3134 mutex_lock(&signal_mutex
);
3135 if (seq
!= signals
[sig
].last_sig_sequence
) {
3136 mutex_unlock(&signal_mutex
);
3139 *mutex_to_lock
= &signal_mutex
;
3140 list_add(&signals
[sig
].wait_list
, list_entry
);
3141 mutex_unlock(&signal_mutex
);
3146 static int os2_notify_socket
[2];
3148 static bool os2_socketpair_af_unix(int result
[2])
3151 struct sockaddr_un sun
;
3157 result
[0] = result
[1] = -1;
3159 lst
= proc_socket(PF_UNIX
, SOCK_STREAM
, 0);
3160 if (unlikely(lst
== -1))
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))
3170 r
= proc_getsockname(lst
, (struct sockaddr
*)&sun
, &len
);
3171 if (unlikely(r
== -1))
3174 r
= proc_listen(lst
, 1);
3175 if (unlikely(r
== -1))
3178 result
[0] = proc_socket(PF_UNIX
, SOCK_STREAM
, 0);
3179 if (unlikely(result
[0] == -1))
3182 r
= proc_connect(result
[0], (struct sockaddr
*)&sun
, sizeof sun
);
3183 if (unlikely(r
== -1))
3187 result
[1] = proc_accept(lst
, (struct sockaddr
*)&sun
, &len
);
3188 if (unlikely(result
[1] == -1))
3192 r
= proc_ioctl(result
[0], FIONBIO
, &one
, sizeof one
);
3193 if (unlikely(r
== -1))
3195 r
= proc_ioctl(result
[1], FIONBIO
, &one
, sizeof one
);
3196 if (unlikely(r
== -1))
3206 if (result
[0] != -1)
3207 proc_soclose(result
[0]);
3208 if (result
[1] != -1)
3209 proc_soclose(result
[1]);
3213 static bool os2_socketpair(int result
[2])
3216 struct sockaddr_in sin
;
3221 if (os2_socketpair_af_unix(result
))
3225 result
[0] = result
[1] = -1;
3227 lst
= proc_socket(PF_INET
, SOCK_STREAM
, 0);
3228 if (unlikely(lst
== -1))
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))
3238 r
= proc_getsockname(lst
, (struct sockaddr
*)&sin
, &len
);
3239 if (unlikely(r
== -1))
3242 r
= proc_listen(lst
, 1);
3243 if (unlikely(r
== -1))
3246 result
[0] = proc_socket(PF_INET
, SOCK_STREAM
, 0);
3247 if (unlikely(result
[0] == -1))
3250 r
= proc_connect(result
[0], (struct sockaddr
*)&sin
, sizeof sin
);
3251 if (unlikely(r
== -1))
3255 result
[1] = proc_accept(lst
, (struct sockaddr
*)&sin
, &len
);
3256 if (unlikely(result
[1] == -1))
3260 r
= proc_ioctl(result
[0], FIONBIO
, &one
, sizeof one
);
3261 if (unlikely(r
== -1))
3263 r
= proc_ioctl(result
[1], FIONBIO
, &one
, sizeof one
);
3264 if (unlikely(r
== -1))
3274 if (result
[0] != -1)
3275 proc_soclose(result
[0]);
3276 if (result
[1] != -1)
3277 proc_soclose(result
[1]);
3281 static void os2_notify(void)
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];
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
))
3302 fatal("error reading the notify socket: %d", er
);
3307 static void os2_shutdown_notify_pipe(void)
3310 r
= proc_shutdown(os2_notify_socket
[0], 2);
3311 if (likely(r
== -1)) {
3313 fatal("error shutting down the notify socket: %d", er
);
3321 handle_t
os_socket(int domain
, int type
, int protocol
, ajla_error_t
*err
)
3324 if (unlikely(!tcpip_loaded
)) {
3325 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not installed");
3328 domain
= os_socket_pf(domain
, err
);
3329 if (unlikely(domain
== -1))
3331 type
= os_socket_type(type
, err
);
3332 if (unlikely(type
== -1))
3334 sock
= proc_socket(domain
, type
, protocol
);
3335 if (unlikely(sock
== -1)) {
3336 fatal_mayfail(error_from_os2_socket(), err
, "socket failed");
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
)
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");
3354 sa
= os_get_sock_addr(addr
, &addr_len
, err
);
3357 r
= (likely(!bnd
) ? proc_connect
: proc_bind
)(h
->h
, sa
, addr_len
);
3358 mem_free_aligned(sa
);
3361 er
= proc_sock_errno();
3364 if (likely(!bnd
) && likely(er
== SOCEINPROGRESS
))
3366 fatal_mayfail(error_from_os2_socket(), err
, "can't %s socket: %d", !bnd
? "connect" : "bind", er
);
3370 bool os_connect_completed(handle_t h
, ajla_error_t
*err
)
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");
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());
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
);
3395 bool os_listen(handle_t h
, ajla_error_t
*err
)
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");
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());
3413 int os_accept(handle_t h
, handle_t
*result
, ajla_error_t
*err
)
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");
3424 r
= proc_accept(h
->h
, NULL
, 0);
3425 if (unlikely(r
== -1)) {
3426 int er
= proc_sock_errno();
3429 if (er
== SOCEAGAIN
)
3430 return OS_RW_WOULDBLOCK
;
3431 fatal_mayfail(error_from_os2_socket(), err
, "accept returned an error: %d", er
);
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
)
3443 struct sockaddr
*sa
;
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");
3452 sa
= mem_align_mayfail(struct sockaddr
*, SOCKADDR_MAX_LEN
, SOCKADDR_ALIGN
, err
);
3455 addrlen
= SOCKADDR_MAX_LEN
;
3457 r
= (!peer
? proc_getsockname
: proc_getpeername
)(h
->h
, sa
, &addrlen
);
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
);
3474 mem_free_aligned(sa
);
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
)
3481 struct sockaddr
*sa
;
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");
3490 f
= translate_flags(os_socket_msg
, flags
, err
);
3491 if (unlikely(f
< 0))
3494 sa
= mem_align_mayfail(struct sockaddr
*, SOCKADDR_MAX_LEN
, SOCKADDR_ALIGN
, err
);
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();
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
;
3517 if (unlikely(!array_init_mayfail(unsigned char, addr
, addr_len
, err
))) {
3518 goto free_ret_error
;
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
);
3531 mem_free_aligned(sa
);
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
)
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");
3546 f
= translate_flags(os_socket_msg
, flags
, err
);
3547 if (unlikely(f
< 0))
3551 if (addr_len
!= 0) {
3552 size_t al
= addr_len
;
3553 sa
= os_get_sock_addr(addr
, &al
, err
);
3556 r
= proc_sendto(h
->h
, buffer
, len
, f
, sa
, al
);
3557 mem_free_aligned(sa
);
3559 r
= proc_send(h
->h
, buffer
, len
, f
);
3562 if (unlikely(r
== -1)) {
3563 int er
= proc_sock_errno();
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
);
3575 bool os_getsockopt(handle_t h
, int level
, int option
, char **buffer
, size_t *buffer_len
, ajla_error_t
*err
)
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");
3586 level
= os_socket_level(level
, err
);
3587 if (unlikely(level
< 0))
3590 option
= os_socket_option(option
, err
);
3591 if (unlikely(level
< 0))
3596 *buffer
= mem_alloc_mayfail(char *, opt_len
, err
);
3597 if (unlikely(!*buffer
))
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
);
3609 *buffer_len
= opt_len
;
3613 bool os_setsockopt(handle_t h
, int level
, int option
, const char *buffer
, size_t buffer_len
, ajla_error_t
*err
)
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");
3623 level
= os_socket_level(level
, err
);
3624 if (unlikely(level
< 0))
3627 option
= os_socket_option(option
, err
);
3628 if (unlikely(level
< 0))
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
);
3642 bool os_getaddrinfo(const char *host
, int port
, struct address
**result
, size_t *result_l
, ajla_error_t
*err
)
3649 if (unlikely(!tcpip_loaded
)) {
3650 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not installed");
3654 if (unlikely(!array_init_mayfail(struct address
, result
, result_l
, err
)))
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");
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");
3669 for (i
= 0; (a
= he
->h_addr_list
[i
]); i
++) {
3670 struct sockaddr_in sin
;
3672 struct address addr
;
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
))
3685 addr
.address_length
= addrlen
;
3687 if (unlikely(!array_add_mayfail(struct address
, result
, result_l
, addr
, &xresult
, err
))) {
3693 if (unlikely(!*result_l
)) {
3694 fatal_mayfail(error_ajla_aux(EC_SYSCALL
, AJLA_ERROR_H_ERRNO
, NO_DATA
), err
, "host not found");
3701 for (i
= 0; i
< *result_l
; i
++)
3702 mem_free((*result
)[i
].address
);
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");
3713 const char *os_decode_error(ajla_error_t attr_unused error
, char attr_unused
*(*tls_buffer
)(void))
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
)
3729 memset(mod
, 0, sizeof mod
);
3730 rc
= DosLoadModule(mod
, sizeof mod
, filename
, &h
);
3731 _control87(CW_DEFAULT
, 0xffff);
3732 if (unlikely(rc
!= 0)) {
3734 if (rc
== ERROR_INTERRUPT
)
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
);
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
)
3753 rc
= DosFreeModule(ptr_to_num(dlh
));
3754 _control87(CW_DEFAULT
, 0xffff);
3755 if (unlikely(rc
!= 0)) {
3756 if (rc
== ERROR_INTERRUPT
)
3758 internal(file_line
, "DosFreeModule returned an error: %lu", rc
);
3762 static const struct {
3766 #include "os_os2_s.inc"
3769 bool os_dlsym(struct dl_handle_t
*dlh
, const char *symbol
, void **result
)
3772 if (dlh
== hdoscalls
) {
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);*/
3778 rc
= DosQueryProcAddr(ptr_to_num(dlh
), 0, symbol
, (PPFN
)result
);
3779 /*debug("symbol %s, result %lu", symbol, rc);*/
3781 if (unlikely(rc
!= 0)) {
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
)
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
,
3813 while (likely(!os2_drain_notify_pipe())) {
3815 size_t select_arg_n
;
3816 size_t select_arg_read
= 0;
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
++) {
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
);
3833 list_del(&thr
->socket_entry
);
3834 thr
->socket_entry
.next
= NULL
;
3836 address_unlock(h
, DEPTH_THUNK
);
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
);
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
++) {
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
;
3866 p
= bsearch(&hndl
, select_arg
, select_arg_read
, sizeof(int), compare_int
);
3868 p
= bsearch(&hndl
, select_arg
+ select_arg_read
, select_arg_n
- select_arg_read
, sizeof(int), compare_int
);
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)
3896 hso32dll
= os_dlopen("SO32DLL", &sink
, NULL
);
3899 htcp32dll
= os_dlopen("TCP32DLL", &sink
, NULL
);
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
) ||
3927 if (!os2_socketpair(os2_notify_socket
))
3930 tcpip_loaded
= true;
3933 bool os2_test_for_32bit_tcpip(const char *mem
)
3939 if (unlikely(!tcpip_loaded
))
3942 r
= proc_send(os2_notify_socket
[1], mem
, 1, 0);
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
);
3960 os_threads_initialized
= false;
3962 os_init_path_to_exe();
3967 rc
= DosQueryHType(n_std_handles
, &htype
, &hattr
);
3973 if (unlikely(n_std_handles
< 3))
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
) {
3988 rc
= proc_DosTmrQueryFreq(&tmr_freq
);
3989 if (unlikely(rc
!= 0))
3991 freq_period_usec
= (long double)1000000 / tmr_freq
;
3992 rc
= proc_DosTmrQueryTime(&qw
);
3993 if (unlikely(rc
!= 0))
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);*/
4007 os_cwd
= os_dir_cwd(NULL
);
4012 os_dir_close(os_cwd
);
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)
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
);
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
);
4063 debug("X2: %d", i
++);
4064 os_pipe(p
, 3, NULL
);
4065 r
= os_read(p
[0], &c
, 1, NULL
);
4067 r
= rand() & 0xffff;
4069 __asm__
volatile("nop":::"memory");
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
);
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)
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
);
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
);
4132 mutex_lock(&deferred_mutex
);
4134 mutex_unlock(&deferred_mutex
);
4135 os2_clean_up_handles();
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
;
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;