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/>.
37 #define WIN32_BUFFER_SIZE 4096
38 #define WIN32_LINE_BUFFER_SIZE 4096
39 #define WIN32_IO_THREAD_STACK_SIZE 4096
41 #define SOCKADDR_MAX_LEN 512
42 #define SOCKADDR_ALIGN 16
44 #define CONNECT_TIMEOUT 500000
46 struct win32_io_thread
{
53 unsigned line_buffer_pos
;
54 unsigned line_buffer_size
;
62 HANDLE terminate_mutex
;
63 HANDLE terminate_event
;
64 atomic_type uchar_efficient_t is_packet_console
;
65 bool packet_is_queued
;
66 struct console_read_packet packet
;
67 unsigned last_buttons
;
81 uint8_t utf8_buffer_size
;
83 struct list wait_list
[2];
85 struct win32_io_thread
*rd
;
86 struct win32_io_thread
*wr
;
88 bool connect_in_progress
;
89 struct list socket_entry
[2];
91 struct list deferred_entry
;
93 char file_name
[FLEXIBLE_ARRAY
];
102 #include "os_com.inc"
105 static HMODULE handle_iphlpa
;
107 static LPCSTR (WINAPI
*fn_GetEnvironmentStrings
)(void);
108 static LPCSTR (WINAPI
*fn_GetEnvironmentStringsA
)(void);
109 static LPCWSTR (WINAPI
*fn_GetEnvironmentStringsW
)(void);
110 static BOOL (WINAPI
*fn_FreeEnvironmentStringsA
)(LPCSTR env
);
111 static BOOL (WINAPI
*fn_FreeEnvironmentStringsW
)(LPCWSTR env
);
112 static LONG (NTAPI
*fn_RtlGetVersion
)(POSVERSIONINFOW lpVersionInformation
);
113 static VOID (NTAPI
*fn_RtlFreeUserThreadStack
)(HANDLE ProcessHandle
, HANDLE ThreadHandle
);
114 static BOOL (WINAPI
*fn_GetDiskFreeSpaceExA
)(LPCSTR lpDirectoryName
, PULARGE_INTEGER lpFreeBytesAvailableToCaller
, PULARGE_INTEGER lpTotalNumberOfBytes
, PULARGE_INTEGER lpTotalNumberOfFreeBytes
);
115 static BOOL (WINAPI
*fn_GetDiskFreeSpaceExW
)(LPWSTR lpDirectoryName
, PULARGE_INTEGER lpFreeBytesAvailableToCaller
, PULARGE_INTEGER lpTotalNumberOfBytes
, PULARGE_INTEGER lpTotalNumberOfFreeBytes
);
116 static BOOL (WINAPI
*fn_CancelIo
)(HANDLE hFile
);
117 static BOOL (WINAPI
*fn_CancelIoEx
)(HANDLE hFile
, LPOVERLAPPED lpOverlapped
);
118 static BOOL (WINAPI
*fn_MoveFileExA
)(LPCSTR lpExistingFileName
, LPCSTR lpNewFileName
, DWORD dwFlags
);
119 static BOOL (WINAPI
*fn_MoveFileExW
)(LPWSTR lpExistingFileName
, LPWSTR lpNewFileName
, DWORD dwFlags
);
120 static BOOL (WINAPI
*fn_FlushInstructionCache
)(HANDLE hProcess
, LPCVOID
*lpBaseAddress
, SIZE_T dwSize
);
121 static DWORD (WINAPI
*fn_GetNetworkParams
)(char *, PULONG pOutBufLen
);
122 static uint64_t (WINAPI
*fn_GetTickCount64
)(void);
124 /* QPC is broken, it sometimes jumps backwards, so don't use it by default */
126 static BOOL (WINAPI
*fn_QueryPerformanceFrequency
)(LARGE_INTEGER
*lpPerformanceCount
);
127 static BOOL (WINAPI
*fn_QueryPerformanceCounter
)(LARGE_INTEGER
*lpPerformanceCount
);
128 static long double perf_multiplier
;
131 typedef struct addrinfo
{
138 struct sockaddr
*ai_addr
;
139 struct addrinfo
*ai_next
;
140 } ADDRINFOA
, *PADDRINFOA
;
142 typedef ADDRINFOA ADDRINFO
, *LPADDRINFO
;
144 #define EAI_NONAME WSAHOST_NOT_FOUND
146 static int (WINAPI
*fn_getaddrinfo
)(const char *nodename
, const char *servname
, const struct addrinfo
*hints
, struct addrinfo
**res
);
147 static void (WINAPI
*fn_freeaddrinfo
)(LPADDRINFO pAddrInfo
);
148 static int (WINAPI
*fn_getnameinfo
)(const struct sockaddr
*sockaddr
, socklen_t sockaddr_len
, char *node
, DWORD node_size
, char *service
, DWORD service_size
, int flags
);
151 static bool os_threads_initialized
;
153 static handle_t win32_std_handles
[3];
157 static struct list deferred_write_list
;
158 static struct list deferred_closed_list
;
159 static mutex_t deferred_mutex
;
161 static bool winsock_supported
;
162 static struct list socket_list
[2];
163 static mutex_t socket_list_mutex
;
166 struct system_error_table_entry
{
168 unsigned short sys_error
;
171 static const struct system_error_table_entry win32_error_to_system_error
[] = {
172 { ERROR_INVALID_FUNCTION
, SYSTEM_ERROR_EINVAL
},
173 { ERROR_FILE_NOT_FOUND
, SYSTEM_ERROR_ENOENT
},
174 { ERROR_PATH_NOT_FOUND
, SYSTEM_ERROR_ENOENT
},
175 { ERROR_TOO_MANY_OPEN_FILES
, SYSTEM_ERROR_EMFILE
},
176 { ERROR_ACCESS_DENIED
, SYSTEM_ERROR_EACCES
},
177 { ERROR_INVALID_HANDLE
, SYSTEM_ERROR_EBADF
},
178 { ERROR_NOT_ENOUGH_MEMORY
, SYSTEM_ERROR_ENOMEM
},
179 { ERROR_OUTOFMEMORY
, SYSTEM_ERROR_ENOMEM
},
180 { ERROR_INVALID_DRIVE
, SYSTEM_ERROR_ENOENT
},
181 { ERROR_NOT_SAME_DEVICE
, SYSTEM_ERROR_EXDEV
},
182 { ERROR_WRITE_PROTECT
, SYSTEM_ERROR_EROFS
},
183 { ERROR_NOT_READY
, SYSTEM_ERROR_ENOMEDIUM
},
184 { ERROR_CRC
, SYSTEM_ERROR_EIO
},
185 { ERROR_SECTOR_NOT_FOUND
, SYSTEM_ERROR_EIO
},
186 { ERROR_SHARING_VIOLATION
, SYSTEM_ERROR_EBUSY
},
187 { ERROR_HANDLE_DISK_FULL
, SYSTEM_ERROR_ENOSPC
},
188 { ERROR_NOT_SUPPORTED
, SYSTEM_ERROR_EOPNOTSUPP
},
189 { ERROR_DEV_NOT_EXIST
, SYSTEM_ERROR_ENXIO
},
190 { ERROR_NETWORK_ACCESS_DENIED
, SYSTEM_ERROR_EACCES
},
191 { ERROR_FILE_EXISTS
, SYSTEM_ERROR_EEXIST
},
192 { ERROR_DRIVE_LOCKED
, SYSTEM_ERROR_EBUSY
},
193 { ERROR_BROKEN_PIPE
, SYSTEM_ERROR_EPIPE
},
194 { ERROR_OPEN_FAILED
, SYSTEM_ERROR_ENOENT
},
195 { ERROR_BUFFER_OVERFLOW
, SYSTEM_ERROR_ENAMETOOLONG
},
196 { ERROR_DISK_FULL
, SYSTEM_ERROR_ENOSPC
},
197 { ERROR_INVALID_NAME
, SYSTEM_ERROR_EINVAL
},
198 { ERROR_NEGATIVE_SEEK
, SYSTEM_ERROR_EINVAL
},
199 { ERROR_SEEK_ON_DEVICE
, SYSTEM_ERROR_ESPIPE
},
200 { ERROR_DIR_NOT_EMPTY
, SYSTEM_ERROR_ENOTEMPTY
},
201 { ERROR_BUSY
, SYSTEM_ERROR_EBUSY
},
202 { ERROR_ALREADY_EXISTS
, SYSTEM_ERROR_EEXIST
},
203 { ERROR_FILENAME_EXCED_RANGE
, SYSTEM_ERROR_ENAMETOOLONG
},
204 { /*ERROR_FILE_TOO_LARGE*/ 223, SYSTEM_ERROR_EOVERFLOW
},
205 { ERROR_NO_DATA
, SYSTEM_ERROR_EPIPE
},
206 { ERROR_DIRECTORY
, SYSTEM_ERROR_ENOTDIR
},
207 { 567, SYSTEM_ERROR_ENOMEM
},
208 { WSAEINTR
, SYSTEM_ERROR_EINTR
},
209 { WSAEBADF
, SYSTEM_ERROR_EBADF
},
210 { WSAEACCES
, SYSTEM_ERROR_EACCES
},
211 { WSAEFAULT
, SYSTEM_ERROR_EFAULT
},
212 { WSAEINVAL
, SYSTEM_ERROR_EINVAL
},
213 { WSAEMFILE
, SYSTEM_ERROR_EMFILE
},
214 { WSAEWOULDBLOCK
, SYSTEM_ERROR_EAGAIN
},
215 { WSAEINPROGRESS
, SYSTEM_ERROR_EINPROGRESS
},
216 { WSAEALREADY
, SYSTEM_ERROR_EALREADY
},
217 { WSAENOTSOCK
, SYSTEM_ERROR_ENOTSOCK
},
218 { WSAEDESTADDRREQ
, SYSTEM_ERROR_EDESTADDRREQ
},
219 { WSAEMSGSIZE
, SYSTEM_ERROR_EMSGSIZE
},
220 { WSAEPROTOTYPE
, SYSTEM_ERROR_EPROTOTYPE
},
221 { WSAENOPROTOOPT
, SYSTEM_ERROR_ENOPROTOOPT
},
222 { WSAEPROTONOSUPPORT
, SYSTEM_ERROR_EPROTONOSUPPORT
},
223 { WSAESOCKTNOSUPPORT
, SYSTEM_ERROR_ESOCKTNOSUPPORT
},
224 { WSAEOPNOTSUPP
, SYSTEM_ERROR_EOPNOTSUPP
},
225 { WSAEPFNOSUPPORT
, SYSTEM_ERROR_EPFNOSUPPORT
},
226 { WSAEAFNOSUPPORT
, SYSTEM_ERROR_EAFNOSUPPORT
},
227 { WSAEADDRINUSE
, SYSTEM_ERROR_EADDRINUSE
},
228 { WSAEADDRNOTAVAIL
, SYSTEM_ERROR_EADDRNOTAVAIL
},
229 { WSAENETDOWN
, SYSTEM_ERROR_ENETDOWN
},
230 { WSAENETUNREACH
, SYSTEM_ERROR_ENETUNREACH
},
231 { WSAENETRESET
, SYSTEM_ERROR_ENETRESET
},
232 { WSAECONNABORTED
, SYSTEM_ERROR_ECONNABORTED
},
233 { WSAECONNRESET
, SYSTEM_ERROR_ECONNRESET
},
234 { WSAENOBUFS
, SYSTEM_ERROR_ENOBUFS
},
235 { WSAEISCONN
, SYSTEM_ERROR_EISCONN
},
236 { WSAENOTCONN
, SYSTEM_ERROR_ENOTCONN
},
237 { WSAESHUTDOWN
, SYSTEM_ERROR_ESHUTDOWN
},
238 { WSAETOOMANYREFS
, SYSTEM_ERROR_ETOOMANYREFS
},
239 { WSAETIMEDOUT
, SYSTEM_ERROR_ETIMEDOUT
},
240 { WSAECONNREFUSED
, SYSTEM_ERROR_ECONNREFUSED
},
241 { WSAELOOP
, SYSTEM_ERROR_ELOOP
},
242 { WSAENAMETOOLONG
, SYSTEM_ERROR_ENAMETOOLONG
},
243 { WSAEHOSTDOWN
, SYSTEM_ERROR_EHOSTDOWN
},
244 { WSAEHOSTUNREACH
, SYSTEM_ERROR_EHOSTUNREACH
},
245 { WSAENOTEMPTY
, SYSTEM_ERROR_ENOTEMPTY
},
246 { WSAEUSERS
, SYSTEM_ERROR_EUSERS
},
247 { WSAEDQUOT
, SYSTEM_ERROR_EDQUOT
},
248 { WSAESTALE
, SYSTEM_ERROR_ESTALE
},
249 { WSAEREMOTE
, SYSTEM_ERROR_EREMOTE
},
250 { WSAEDISCON
, SYSTEM_ERROR_EPIPE
},
253 #ifndef INVALID_FILE_ATTRIBUTES
254 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
257 #ifndef FILE_READ_ONLY
258 #define FILE_READ_ONLY 8
261 static ajla_error_t
error_from_win32(int ec
, DWORD rc
)
264 binary_search(size_t, n_array_elements(win32_error_to_system_error
), r
, win32_error_to_system_error
[r
].errn
== rc
, win32_error_to_system_error
[r
].errn
< rc
, return error_ajla_aux(ec
, AJLA_ERROR_WIN32
, rc
));
265 return error_ajla_aux(ec
, AJLA_ERROR_SYSTEM
, win32_error_to_system_error
[r
].sys_error
);
268 static ajla_error_t
error_from_win32_socket(int errn
)
270 if (unlikely(!winsock_supported
)) {
271 return error_ajla(EC_SYSCALL
, AJLA_ERROR_NOT_SUPPORTED
);
273 return error_from_win32(EC_SYSCALL
, errn
);
276 uint32_t os_get_last_error(void)
278 return GetLastError();
281 uint32_t os_get_last_socket_error(void)
283 return WSAGetLastError();
288 #define is_winnt() true
292 static bool is_winnt(void)
294 return (GetVersion() & 0x80000000U
) == 0;
299 static WCHAR
*utf8_to_wchar(const char *str
, ajla_error_t
*err
)
305 if (unlikely(!array_init_mayfail(WCHAR
, &r
, &l
, err
)))
309 unsigned char c
= *str
++;
310 unsigned char d
, e
, f
;
312 if (likely(c
< 0x80)) {
314 } else if (unlikely(c
< 0xc0)) {
316 } else if (likely(c
< 0xe0)) {
318 if (unlikely(d
< 0x80) || unlikely(d
>= 0xc0))
320 u
= ((uint32_t)(c
& 0x1f) << 6) | (d
& 0x3f);
321 if (unlikely(u
< 0x80))
323 } else if (likely(c
< 0xf0)) {
325 if (unlikely(d
< 0x80) || unlikely(d
>= 0xc0))
328 if (unlikely(e
< 0x80) || unlikely(e
>= 0xc0))
330 u
= ((uint32_t)(c
& 0xf) << 12) | ((uint32_t)(d
& 0x3f) << 6) | (e
& 0x3f);
331 if (unlikely(u
< 0x800))
333 } else if (likely(c
< 0xf8)) {
335 if (unlikely(d
< 0x80) || unlikely(d
>= 0xc0))
338 if (unlikely(e
< 0x80) || unlikely(e
>= 0xc0))
341 if (unlikely(f
< 0x80) || unlikely(f
>= 0xc0))
343 u
= ((uint32_t)(c
& 0x7) << 18) | ((uint32_t)(d
& 0x3f) << 12) | ((uint32_t)(e
& 0x3f) << 6) | (f
& 0x3f);
344 if (unlikely(u
< 0x10000) || unlikely(u
>= 0x110000))
350 if (unlikely(u
>= 0xd800) && unlikely(u
< 0xe000))
352 if (unlikely(!array_add_mayfail(WCHAR
, &r
, &l
, u
, NULL
, err
)))
357 u1
= (u
>> 10) | 0xd800;
358 u2
= (u
& 0x3ff) | 0xdc00;
359 if (unlikely(!array_add_mayfail(WCHAR
, &r
, &l
, u1
, NULL
, err
)))
361 if (unlikely(!array_add_mayfail(WCHAR
, &r
, &l
, u2
, NULL
, err
)))
366 if (unlikely(!array_add_mayfail(WCHAR
, &r
, &l
, 0, NULL
, err
)))
373 e
= error_from_win32(EC_SYSCALL
, AJLA_ERROR_INVALID_OPERATION
);
374 fatal_mayfail(e
, err
, "invalid utf-8");
378 static char *wchar_to_utf8(char *result
, const WCHAR
*str
, ajla_error_t
*err
)
384 if (likely(!result
)) {
385 if (unlikely(!array_init_mayfail(char, &r
, &l
, err
)))
392 #define emit_char(ch) \
394 if (likely(!result)) { \
395 if (unlikely(!array_add_mayfail(char, &r, &l, ch, NULL, err)))\
398 result[l++] = (ch); \
403 uint32_t w
= (uint16_t)*str
++;
404 if (unlikely((w
& 0xfc00) == 0xd800)) {
405 uint32_t hi
= (w
& 0x3ff) << 10;
406 uint32_t lo
= (uint16_t)*str
++;
407 if (unlikely((lo
& 0xfc00) != 0xdc00))
410 w
= hi
+ lo
+ 0x10000;
412 if (likely(w
< 0x80)) {
414 } else if (likely(w
< 0x800)) {
415 emit_char(0xc0 | (w
>> 6));
416 emit_char(0x80 | (w
& 0x3f));
417 } else if (likely(w
< 0x10000)) {
418 emit_char(0xe0 | (w
>> 12));
419 emit_char(0x80 | ((w
>> 6) & 0x3f));
420 emit_char(0x80 | (w
& 0x3f));
421 } else if (likely(w
< 0x110000)) {
422 emit_char(0xf0 | (w
>> 18));
423 emit_char(0x80 | ((w
>> 12) & 0x3f));
424 emit_char(0x80 | ((w
>> 6) & 0x3f));
425 emit_char(0x80 | (w
& 0x3f));
435 if (likely(!result
)) {
436 array_finish(char, &r
, &l
);
444 e
= error_from_win32(EC_SYSCALL
, AJLA_ERROR_INVALID_OPERATION
);
445 fatal_mayfail(e
, err
, "invalid utf-16");
450 void os_block_signals(sig_state_t attr_unused
*set
)
454 void os_unblock_signals(const sig_state_t attr_unused
*set
)
460 warning("stop not supported on Windows");
463 void os_background(void)
467 bool os_foreground(void)
473 static void win32_close_handle(HANDLE h
)
475 if (unlikely(!CloseHandle(h
)))
476 internal(file_line
, "CloseHandle failed: %u", GetLastError());
479 static void win32_close_change_notification_handle(HANDLE h
)
481 if (unlikely(!FindCloseChangeNotification(h
)))
482 internal(file_line
, "FindCloseChangeNotification failed: %u", GetLastError());
485 static void win32_close_socket(SOCKET s
)
487 if (unlikely(closesocket(s
) == SOCKET_ERROR
))
488 warning("closesocket returned an error: %u", WSAGetLastError());
491 static void win32_set_event(HANDLE h
)
493 if (unlikely(!SetEvent(h
)))
494 internal(file_line
, "SetEvent failed: %u", GetLastError());
498 static handle_t
win32_hfile_to_handle(HANDLE hfile
, int flags
, bool overlapped
, char *file_name
, ajla_error_t
*err
)
500 size_t file_name_len
;
502 DWORD type
, gle
, cmode
;
505 type
= GetFileType(hfile
);
506 if (unlikely(type
== FILE_TYPE_UNKNOWN
) && (gle
= GetLastError())) {
507 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
508 fatal_mayfail(e
, err
, "can't get file type: %s", error_decode(e
));
509 win32_close_handle(hfile
);
512 type
&= ~FILE_TYPE_REMOTE
;
514 file_name_len
= strlen(file_name
);
516 h
= struct_alloc_array_mayfail(mem_calloc_mayfail
, struct win32_handle
, file_name
, file_name_len
+ 1, err
);
518 win32_close_handle(hfile
);
522 memcpy(h
->file_name
, file_name
, file_name_len
+ 1);
524 if (GetConsoleMode(hfile
, &cmode
)) {
525 h
->is_console
= true;
528 h
->is_overlapped
= overlapped
;
531 h
->s
= INVALID_SOCKET
;
533 if (type
== FILE_TYPE_DISK
)
534 flags
&= ~O_NONBLOCK
;
537 list_init(&h
->wait_list
[0]);
538 list_init(&h
->wait_list
[1]);
540 obj_registry_insert(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
545 static handle_t
win32_socket_to_handle(SOCKET sock
, ajla_error_t
*err
)
550 if (unlikely(ioctlsocket(sock
, FIONBIO
, &one
) == SOCKET_ERROR
)) {
551 fatal_mayfail(error_from_win32_socket(WSAGetLastError()), err
, "could not set socket non-blocking");
552 win32_close_socket(sock
);
556 h
= struct_alloc_array_mayfail(mem_calloc_mayfail
, struct win32_handle
, file_name
, 1, err
);
558 win32_close_socket(sock
);
562 h
->h
= INVALID_HANDLE_VALUE
;
564 h
->flags
= O_RDWR
| O_NONBLOCK
;
566 list_init(&h
->wait_list
[0]);
567 list_init(&h
->wait_list
[1]);
569 obj_registry_insert(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
574 static inline bool handle_is_socket(handle_t h
)
576 return h
->s
!= INVALID_SOCKET
;
579 uintptr_t os_handle_to_number(handle_t h
)
581 if (handle_is_socket(h
))
584 return ptr_to_num(h
->h
);
587 handle_t
os_number_to_handle(uintptr_t n
, bool sckt
, ajla_error_t
*err
)
590 return win32_hfile_to_handle(num_to_ptr(n
), O_RDWR
, false, "", err
);
592 if (unlikely(!winsock_supported
)) {
593 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not configured");
596 return win32_socket_to_handle(n
, err
);
600 static void win32_clean_up_handles(void);
602 handle_t
os_open(dir_handle_t dir
, const char *path
, int flags
, int mode
, ajla_error_t
*err
)
605 DWORD access
, disposition
, attrs
, share_mode
;
610 win32_clean_up_handles();
612 joined
= os_join_paths(dir
, path
, false, err
);
613 if (unlikely(!joined
))
617 case O_RDONLY
: access
= GENERIC_READ
; break;
618 case O_WRONLY
: access
= GENERIC_WRITE
; break;
619 case O_RDWR
: access
= GENERIC_READ
| GENERIC_WRITE
; break;
620 default: internal(file_line
, "os_open: invalid flags %x", flags
); return NULL
;
623 switch (flags
& (O_CREAT
| O_EXCL
| O_TRUNC
)) {
624 case 0: disposition
= OPEN_EXISTING
; break;
625 case O_CREAT
: disposition
= OPEN_ALWAYS
; break;
626 case O_CREAT
| O_EXCL
: disposition
= CREATE_NEW
; break;
627 case O_TRUNC
: disposition
= TRUNCATE_EXISTING
; break;
628 case O_TRUNC
| O_CREAT
: disposition
= CREATE_ALWAYS
; break;
629 case O_TRUNC
| O_CREAT
| O_EXCL
: disposition
= CREATE_NEW
; break;
630 default: internal(file_line
, "os_open: invalid flags %x", flags
); return NULL
;
635 attrs
|= FILE_READ_ONLY
;
637 share_mode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
640 WCHAR
*w
= utf8_to_wchar(joined
, err
);
645 hfile
= CreateFileW(w
, access
, share_mode
, NULL
, disposition
, attrs
, NULL
);
646 gle
= GetLastError();
649 hfile
= CreateFileA(joined
, access
, share_mode
, NULL
, disposition
, attrs
, NULL
);
650 gle
= GetLastError();
652 if (unlikely(hfile
== INVALID_HANDLE_VALUE
)) {
654 if (gle
== ERROR_INVALID_PARAMETER
&& share_mode
& FILE_SHARE_DELETE
) {
655 share_mode
&= ~FILE_SHARE_DELETE
;
658 e
= error_from_win32(EC_SYSCALL
, gle
);
659 fatal_mayfail(e
, err
, "can't open file '%s': %s", joined
, error_decode(e
));
663 h
= win32_hfile_to_handle(hfile
, flags
, false, joined
, err
);
668 static mutex_t pipe_count_mutex
;
669 static uint64_t pipe_count
;
671 bool os_pipe(handle_t result
[2], int nonblock_flags
, ajla_error_t
*err
)
674 bool overlapped
= false;
676 win32_clean_up_handles();
678 if (unlikely(!fn_CancelIoEx
) && likely(fn_CancelIo
!= NULL
)) {
682 if (likely(os_threads_initialized
))
683 mutex_lock(&pipe_count_mutex
);
685 if (likely(os_threads_initialized
))
686 mutex_unlock(&pipe_count_mutex
);
687 sprintf(name
, "\\\\.\\pipe\\ajla-%x-%08x%08x", (unsigned)GetCurrentProcessId(), (unsigned)(pc
>> 32), (unsigned)pc
);
688 h1
= CreateNamedPipeA(name
, PIPE_ACCESS_INBOUND
| FILE_FLAG_OVERLAPPED
, PIPE_TYPE_BYTE
| PIPE_WAIT
, 1, 0, 0, 0, NULL
);
689 if (unlikely(h1
== INVALID_HANDLE_VALUE
)) {
691 DWORD gle
= GetLastError();
692 if (gle
== ERROR_CALL_NOT_IMPLEMENTED
)
694 if (gle
== ERROR_PIPE_BUSY
)
696 e
= error_from_win32(EC_SYSCALL
, gle
);
697 fatal_mayfail(e
, err
, "can't create named pipe: %s", error_decode(e
));
700 h2
= CreateFileA(name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OVERLAPPED
, NULL
);
701 if (unlikely(h2
== INVALID_HANDLE_VALUE
)) {
702 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
703 fatal_mayfail(e
, err
, "can't connect to create named pipe: %s", error_decode(e
));
704 win32_close_handle(h1
);
710 if (unlikely(!CreatePipe(&h1
, &h2
, NULL
, 0))) {
711 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
712 fatal_mayfail(e
, err
, "can't create pipe: %s", error_decode(e
));
717 result
[0] = win32_hfile_to_handle(h1
, O_RDONLY
| (nonblock_flags
& 1 ? O_NONBLOCK
: 0), overlapped
, "", err
);
718 if (unlikely(!result
[0])) {
719 win32_close_handle(h1
);
722 result
[1] = win32_hfile_to_handle(h2
, O_WRONLY
| (nonblock_flags
& 2 ? O_NONBLOCK
: 0), overlapped
, "", err
);
723 if (unlikely(!result
[1])) {
731 static void win32_terminate_io_thread(struct win32_io_thread
*thr
);
733 static void os_free_handle(handle_t h
, bool should_close
)
735 ajla_assert_lo(list_is_empty(&h
->wait_list
[0]), (file_line
, "os_free_handle: freeing handle when there are processes waiting for read"));
736 ajla_assert_lo(list_is_empty(&h
->wait_list
[1]), (file_line
, "os_free_handle: freeing handle when there are processes waiting for write"));
737 obj_registry_remove(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
739 win32_terminate_io_thread(h
->rd
);
742 address_lock(h
, DEPTH_THUNK
);
743 if (h
->wr
->buffer_len
!= 0 && !h
->wr
->err
) {
745 h
->wr
->should_close
= should_close
;
746 mutex_lock(&deferred_mutex
);
747 list_add(&deferred_write_list
, &h
->deferred_entry
);
748 mutex_unlock(&deferred_mutex
);
749 address_unlock(h
, DEPTH_THUNK
);
752 address_unlock(h
, DEPTH_THUNK
);
753 win32_terminate_io_thread(h
->wr
);
755 if (likely(should_close
)) {
756 if (handle_is_socket(h
)) {
757 mutex_lock(&socket_list_mutex
);
758 if (h
->socket_entry
[0].next
!= NULL
)
759 list_del(&h
->socket_entry
[0]);
760 if (h
->socket_entry
[1].next
!= NULL
)
761 list_del(&h
->socket_entry
[1]);
762 mutex_unlock(&socket_list_mutex
);
763 win32_close_socket(h
->s
);
765 win32_close_handle(h
->h
);
771 void os_close(handle_t h
)
773 os_free_handle(h
, true);
776 unsigned os_n_std_handles(void)
781 handle_t
os_get_std_handle(unsigned h
)
783 return win32_std_handles
[h
];
786 static HANDLE
get_std_handle(unsigned u
)
791 case 0: s
= STD_INPUT_HANDLE
; break;
792 case 1: s
= STD_OUTPUT_HANDLE
; break;
793 case 2: s
= STD_ERROR_HANDLE
; break;
794 default: internal(file_line
, "get_std_handle: invalid handle %u", u
);
797 if (unlikely(h
== INVALID_HANDLE_VALUE
)) {
798 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
799 fatal("can't get standard handle %u: %s", u
, error_decode(e
));
804 static void win32_clean_up_handles(void)
806 if (!list_is_empty(&deferred_closed_list
)) {
807 mutex_lock(&deferred_mutex
);
808 while (!list_is_empty(&deferred_closed_list
)) {
809 handle_t h
= get_struct(deferred_closed_list
.prev
, struct win32_handle
, deferred_entry
);
810 list_del(&h
->deferred_entry
);
811 obj_registry_insert(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
812 os_free_handle(h
, false);
814 mutex_unlock(&deferred_mutex
);
819 static void wait_for_event(HANDLE event
)
821 DWORD r
= WaitForSingleObject(event
, INFINITE
);
822 if (likely(r
== WAIT_OBJECT_0
))
824 if (r
== WAIT_FAILED
)
825 internal(file_line
, "WaitForSingleObject failed: %u", GetLastError());
826 internal(file_line
, "WaitForSingleObject returned: %x", r
);
829 static bool wait_for_event_timeout(HANDLE event
)
831 DWORD r
= WaitForSingleObject(event
, 1);
832 if (likely(r
== WAIT_OBJECT_0
))
834 if (likely(r
== WAIT_TIMEOUT
))
836 if (r
== WAIT_FAILED
)
837 internal(file_line
, "WaitForSingleObject failed: %u", GetLastError());
838 internal(file_line
, "WaitForSingleObject returned: %x", r
);
842 static unsigned wait_for_2_events(HANDLE event1
, HANDLE event2
)
848 r
= WaitForMultipleObjects(2, a
, FALSE
, INFINITE
);
849 if (likely(r
>= WAIT_OBJECT_0
+ zero
) && likely(r
< WAIT_OBJECT_0
+ 2))
850 return r
- WAIT_OBJECT_0
;
851 if (unlikely(r
== WAIT_FAILED
))
852 internal(file_line
, "WaitForMultipleObjects failed: %u", GetLastError());
853 internal(file_line
, "WaitForMultipleObjects returned: %x", r
);
857 static void unlock_mutex(HANDLE mutex
)
859 if (unlikely(!ReleaseMutex(mutex
)))
860 internal(file_line
, "ReleaseMutex failed: %u", GetLastError());
863 static bool use_terminate_thread(struct win32_io_thread
*thr
)
865 return unlikely(!fn_CancelIoEx
) && !thr
->h
->is_console
&& !thr
->h
->is_overlapped
;
868 static void lock_io_thread(struct win32_io_thread
*thr
)
870 if (unlikely(use_terminate_thread(thr
)))
871 wait_for_event(thr
->terminate_mutex
);
872 address_lock(thr
->h
, DEPTH_THUNK
);
875 static void unlock_io_thread(struct win32_io_thread
*thr
)
877 address_unlock(thr
->h
, DEPTH_THUNK
);
878 if (unlikely(use_terminate_thread(thr
)))
879 unlock_mutex(thr
->terminate_mutex
);
882 static int read_console_packet(struct win32_io_thread
*thr
, struct console_read_packet
*p
)
887 bool wnt
= is_winnt();
889 w
= wait_for_2_events(thr
->terminate_event
, thr
->h
->h
);
892 if (unlikely(!load_relaxed(&thr
->is_packet_console
)))
895 if (unlikely(!ReadConsoleInputW(thr
->h
->h
, &ev
, 1, &nr
)))
898 if (unlikely(!ReadConsoleInputA(thr
->h
->h
, &ev
, 1, &nr
)))
901 memset(p
, 0, sizeof(struct console_read_packet
));
902 if (ev
.EventType
== KEY_EVENT
&& ev
.Event
.KeyEvent
.bKeyDown
) {
903 /*debug("%x - %x - %x", ev.Event.KeyEvent.uChar.AsciiChar, ev.Event.KeyEvent.wVirtualKeyCode, ev.Event.KeyEvent.dwControlKeyState);*/
905 p
->u
.k
.vkey
= ev
.Event
.KeyEvent
.wVirtualKeyCode
;
906 p
->u
.k
.ctrl
= ev
.Event
.KeyEvent
.dwControlKeyState
;
908 p
->u
.k
.key
= ev
.Event
.KeyEvent
.uChar
.UnicodeChar
;
910 p
->u
.k
.key
= (unsigned char)ev
.Event
.KeyEvent
.uChar
.AsciiChar
;
911 p
->u
.k
.cp
= GetConsoleCP();
915 if (ev
.EventType
== MOUSE_EVENT
) {
916 CONSOLE_SCREEN_BUFFER_INFO csbi
;
919 for (i
= 2; i
>= 1; i
--) {
920 g
= get_std_handle(i
);
921 if (likely(GetConsoleScreenBufferInfo(g
, &csbi
)))
927 p
->u
.m
.x
= ev
.Event
.MouseEvent
.dwMousePosition
.X
- csbi
.srWindow
.Left
;
928 p
->u
.m
.y
= ev
.Event
.MouseEvent
.dwMousePosition
.Y
- csbi
.srWindow
.Top
;
929 p
->u
.m
.prev_buttons
= thr
->last_buttons
;
930 p
->u
.m
.buttons
= thr
->last_buttons
= ev
.Event
.MouseEvent
.dwButtonState
& 0x1f;
931 if (ev
.Event
.MouseEvent
.dwEventFlags
& 4)
932 p
->u
.m
.wy
= ev
.Event
.MouseEvent
.dwButtonState
& 0x80000000U
? 1 : -1;
933 if (ev
.Event
.MouseEvent
.dwEventFlags
& 8)
934 p
->u
.m
.wx
= ev
.Event
.MouseEvent
.dwButtonState
& 0x80000000U
? 1 : -1;
936 /*debug("%u %u %x %x", ev.Event.MouseEvent.dwMousePosition.X, ev.Event.MouseEvent.dwMousePosition.Y, ev.Event.MouseEvent.dwButtonState, ev.Event.MouseEvent.dwEventFlags);*/
941 static void echo(struct win32_io_thread
*thr
, WCHAR ch
)
946 if (thr
->h
->tc_flags
& IO_Stty_Flag_Noecho
)
948 for (i
= 2; i
>= 1; i
--) {
949 g
= get_std_handle(i
);
950 if (GetConsoleMode(g
, &cmode
)) {
953 WriteConsoleW(g
, &ch
, 1, &wr
, NULL
);
956 WriteConsoleA(g
, &cha
, 1, &wr
, NULL
);
963 static BOOL
read_console(struct win32_io_thread
*thr
, char *buffer
, unsigned len
, DWORD
*rd
)
971 if (thr
->line_buffer_pos
) {
972 unsigned tx
= min(len
, thr
->line_buffer_pos
);
973 unsigned rem
= thr
->line_buffer_size
- thr
->line_buffer_pos
;
974 memcpy(buffer
, thr
->line_buffer
, tx
);
975 memmove(thr
->line_buffer
, thr
->line_buffer
+ tx
, rem
);
976 thr
->line_buffer_pos
-= tx
;
977 thr
->line_buffer_size
-= tx
;
981 if (unlikely(thr
->line_buffer_eof
) && !thr
->line_buffer_size
)
983 w
= wait_for_2_events(thr
->terminate_event
, thr
->h
->h
);
986 if (unlikely(load_relaxed(&thr
->is_packet_console
)))
989 if (unlikely(!ReadConsoleInputW(thr
->h
->h
, &ev
, 1, &nr
)))
991 ch
= ev
.Event
.KeyEvent
.uChar
.UnicodeChar
;
993 if (unlikely(!ReadConsoleInputA(thr
->h
->h
, &ev
, 1, &nr
)))
995 ch
= ev
.Event
.KeyEvent
.uChar
.AsciiChar
;
997 if (ev
.EventType
== KEY_EVENT
&& ev
.Event
.KeyEvent
.bKeyDown
&& ch
&& !thr
->line_buffer_eof
) {
998 if (ch
== 26 && !(thr
->h
->tc_flags
& IO_Stty_Flag_Raw
)) {
999 thr
->line_buffer_eof
= true;
1000 thr
->line_buffer_pos
= thr
->line_buffer_size
;
1001 } else if (ch
== 8 && !(thr
->h
->tc_flags
& IO_Stty_Flag_Raw
)) {
1002 if (thr
->line_buffer_size
> thr
->line_buffer_pos
) {
1006 thr
->line_buffer_size
--;
1008 } else if ((ch
== 10 || ch
== 13) && !(thr
->h
->tc_flags
& IO_Stty_Flag_Raw
)) {
1009 if (thr
->line_buffer_size
<= WIN32_LINE_BUFFER_SIZE
- 2) {
1010 thr
->line_buffer
[thr
->line_buffer_size
++] = 13;
1011 thr
->line_buffer
[thr
->line_buffer_size
++] = 10;
1012 thr
->line_buffer_pos
= thr
->line_buffer_size
;
1023 if (unlikely(!wchar_to_utf8(ch_buffer
, wchstr
, NULL
)))
1024 goto skip_invalid_char
;
1029 ch_len
= strlen(ch_buffer
);
1030 if (thr
->line_buffer_size
<= WIN32_LINE_BUFFER_SIZE
- 2 - ch_len
) {
1031 memcpy(&thr
->line_buffer
[thr
->line_buffer_size
], ch_buffer
, ch_len
);
1032 thr
->line_buffer_size
+= ch_len
;
1033 if (thr
->h
->tc_flags
& IO_Stty_Flag_Raw
)
1034 thr
->line_buffer_pos
= thr
->line_buffer_size
;
1043 static BOOL
read_overlapped(struct win32_io_thread
*thr
, char *buffer
, unsigned len
, DWORD
*rd
)
1047 memset(&ovl
, 0, sizeof ovl
);
1048 if (unlikely(!ReadFile(thr
->h
->h
, buffer
, len
, rd
, &ovl
))) {
1049 DWORD gle
= GetLastError();
1050 if (gle
!= ERROR_IO_PENDING
)
1053 w
= wait_for_2_events(thr
->terminate_event
, thr
->h
->h
);
1055 if (unlikely(!fn_CancelIo(thr
->h
->h
)))
1056 internal(file_line
, "CancelIo failed: %u", GetLastError());
1058 return GetOverlappedResult(thr
->h
->h
, &ovl
, rd
, TRUE
);
1061 static DWORD WINAPI
win32_read_thread(LPVOID thr_
)
1063 struct win32_io_thread
*thr
= thr_
;
1064 if (unlikely(use_terminate_thread(thr
)))
1065 win32_set_event(thr
->startup_event
);
1067 lock_io_thread(thr
);
1068 if (unlikely(thr
->err
) || unlikely(thr
->eof
) || unlikely(thr
->need_terminate
)) {
1069 unlock_io_thread(thr
);
1071 } else if (load_relaxed(&thr
->is_packet_console
)) {
1074 if (thr
->packet_is_queued
)
1075 goto wait_for_space
;
1076 unlock_io_thread(thr
);
1077 b
= read_console_packet(thr
, &thr
->packet
);
1078 gle
= GetLastError();
1079 lock_io_thread(thr
);
1080 if (unlikely(b
< 0)) {
1081 if (unlikely(gle
== ERROR_OPERATION_ABORTED
)) {
1082 } if (likely(gle
== ERROR_BROKEN_PIPE
)) {
1088 if (likely(load_relaxed(&thr
->is_packet_console
)))
1089 thr
->packet_is_queued
= true;
1091 call(wake_up_wait_list
)(&thr
->h
->wait_list
[0], address_get_mutex(thr
->h
, DEPTH_THUNK
), false);
1092 if (unlikely(use_terminate_thread(thr
)))
1093 unlock_mutex(thr
->terminate_mutex
);
1094 } else if (thr
->buffer_len
< WIN32_BUFFER_SIZE
) {
1098 size_t ptr
= (thr
->buffer_pos
+ thr
->buffer_len
) % WIN32_BUFFER_SIZE
;
1099 size_t len
= thr
->buffer_pos
<= ptr
? WIN32_BUFFER_SIZE
- ptr
: thr
->buffer_pos
- ptr
;
1100 unlock_io_thread(thr
);
1101 if (thr
->h
->is_console
)
1102 b
= read_console(thr
, thr
->buffer
+ ptr
, len
, &rd
);
1103 else if (thr
->h
->is_overlapped
)
1104 b
= read_overlapped(thr
, thr
->buffer
+ ptr
, len
, &rd
);
1106 b
= ReadFile(thr
->h
->h
, thr
->buffer
+ ptr
, len
, &rd
, NULL
);
1107 gle
= GetLastError();
1108 lock_io_thread(thr
);
1109 thr
->buffer_len
+= rd
;
1111 if (unlikely(gle
== ERROR_OPERATION_ABORTED
)) {
1112 } if (likely(gle
== ERROR_BROKEN_PIPE
)) {
1118 if (!load_relaxed(&thr
->is_packet_console
))
1121 call(wake_up_wait_list
)(&thr
->h
->wait_list
[0], address_get_mutex(thr
->h
, DEPTH_THUNK
), false);
1122 if (unlikely(use_terminate_thread(thr
)))
1123 unlock_mutex(thr
->terminate_mutex
);
1126 if (unlikely(!ResetEvent(thr
->data_event
)))
1127 internal(file_line
, "ResetEvent failed: %u", GetLastError());
1128 unlock_io_thread(thr
);
1129 wait_for_event(thr
->data_event
);
1135 static BOOL
write_overlapped(struct win32_io_thread
*thr
, char *buffer
, unsigned len
, DWORD
*wr
)
1139 memset(&ovl
, 0, sizeof ovl
);
1140 if (unlikely(!WriteFile(thr
->h
->h
, buffer
, len
, wr
, &ovl
))) {
1141 DWORD gle
= GetLastError();
1142 if (gle
!= ERROR_IO_PENDING
)
1145 w
= wait_for_2_events(thr
->terminate_event
, thr
->h
->h
);
1147 if (unlikely(!fn_CancelIo(thr
->h
->h
)))
1148 internal(file_line
, "CancelIo failed: %u", GetLastError());
1150 return GetOverlappedResult(thr
->h
->h
, &ovl
, wr
, TRUE
);
1153 static DWORD WINAPI
win32_write_thread(LPVOID thr_
)
1155 struct win32_io_thread
*thr
= thr_
;
1156 if (unlikely(use_terminate_thread(thr
)))
1157 win32_set_event(thr
->startup_event
);
1159 lock_io_thread(thr
);
1160 if (unlikely(thr
->err
) || unlikely(thr
->need_terminate
)) {
1161 if (thr
->eof
&& !thr
->need_terminate
)
1163 unlock_io_thread(thr
);
1165 } else if (thr
->buffer_len
) {
1169 size_t len
= minimum(thr
->buffer_len
, WIN32_BUFFER_SIZE
- thr
->buffer_pos
);
1170 unlock_io_thread(thr
);
1171 if (thr
->h
->is_overlapped
)
1172 b
= write_overlapped(thr
, thr
->buffer
+ thr
->buffer_pos
, len
, &wr
);
1174 b
= WriteFile(thr
->h
->h
, thr
->buffer
+ thr
->buffer_pos
, len
, &wr
, NULL
);
1175 gle
= GetLastError();
1176 lock_io_thread(thr
);
1177 thr
->buffer_pos
= (thr
->buffer_pos
+ wr
) % WIN32_BUFFER_SIZE
;
1178 thr
->buffer_len
-= wr
;
1180 if (gle
!= ERROR_OPERATION_ABORTED
)
1183 call(wake_up_wait_list
)(&thr
->h
->wait_list
[1], address_get_mutex(thr
->h
, DEPTH_THUNK
), false);
1184 if (unlikely(use_terminate_thread(thr
)))
1185 unlock_mutex(thr
->terminate_mutex
);
1186 } else if (unlikely(thr
->eof
)) {
1188 thr
->buffer_len
= 0;
1189 if (thr
->should_close
)
1190 win32_close_handle(thr
->h
->h
);
1191 mutex_lock(&deferred_mutex
);
1192 list_del(&thr
->h
->deferred_entry
);
1193 list_add(&deferred_closed_list
, &thr
->h
->deferred_entry
);
1194 mutex_unlock(&deferred_mutex
);
1195 unlock_io_thread(thr
);
1198 if (unlikely(!ResetEvent(thr
->data_event
)))
1199 internal(file_line
, "ResetEvent failed: %u", GetLastError());
1200 unlock_io_thread(thr
);
1201 wait_for_event(thr
->data_event
);
1207 static bool win32_create_io_thread(handle_t h
, struct win32_io_thread
**pthr
, LPTHREAD_START_ROUTINE win32_thread_function
, ajla_error_t
*err
)
1209 struct win32_io_thread
*thr
;
1211 thr
= mem_calloc_mayfail(struct win32_io_thread
*, sizeof(struct win32_io_thread
), err
);
1218 thr
->buffer_pos
= thr
->buffer_len
= 0;
1220 thr
->buffer
= mem_alloc_mayfail(char *, WIN32_BUFFER_SIZE
, err
);
1221 if (unlikely(!thr
->buffer
))
1223 thr
->data_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
1224 if (unlikely(!thr
->data_event
)) {
1225 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1226 fatal_mayfail(e
, err
, "can't create event: %s", error_decode(e
));
1229 if (unlikely(use_terminate_thread(thr
))) {
1230 thr
->startup_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
1231 if (unlikely(!thr
->startup_event
)) {
1232 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1233 fatal_mayfail(e
, err
, "can't create event: %s", error_decode(e
));
1236 thr
->terminate_mutex
= CreateMutexA(NULL
, FALSE
, NULL
);
1237 if (unlikely(!thr
->terminate_mutex
)) {
1238 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1239 fatal_mayfail(e
, err
, "can't create mutex: %s", error_decode(e
));
1243 if (h
->is_console
|| h
->is_overlapped
) {
1244 thr
->terminate_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
1245 if (unlikely(!thr
->terminate_event
)) {
1246 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1247 fatal_mayfail(e
, err
, "can't create event: %s", error_decode(e
));
1251 if (h
->is_console
) {
1252 thr
->line_buffer
= mem_alloc_mayfail(char *, WIN32_LINE_BUFFER_SIZE
, err
);
1253 if (unlikely(!thr
->line_buffer
))
1257 thr
->thread
= CreateThread(NULL
, WIN32_IO_THREAD_STACK_SIZE
, win32_thread_function
, thr
, 0, &threadid
);
1258 if (unlikely(!thr
->thread
)) {
1259 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1260 fatal_mayfail(e
, err
, "can't create thread: %s", error_decode(e
));
1264 if (unlikely(use_terminate_thread(thr
))) {
1265 wait_for_event(thr
->startup_event
);
1266 win32_close_handle(thr
->startup_event
);
1273 mem_free(thr
->line_buffer
);
1275 if (h
->is_console
|| h
->is_overlapped
)
1276 win32_close_handle(thr
->terminate_event
);
1278 if (unlikely(use_terminate_thread(thr
)))
1279 win32_close_handle(thr
->terminate_mutex
);
1281 if (unlikely(use_terminate_thread(thr
)))
1282 win32_close_handle(thr
->startup_event
);
1284 win32_close_handle(thr
->data_event
);
1286 mem_free(thr
->buffer
);
1293 static void win32_terminate_io_thread(struct win32_io_thread
*thr
)
1296 if (unlikely(use_terminate_thread(thr
))) {
1297 wait_for_event(thr
->terminate_mutex
);
1298 if (likely(fn_RtlFreeUserThreadStack
!= NULL
)) {
1300 if (unlikely(SuspendThread(thr
->thread
) == (DWORD
)-1)) {
1301 gle
= GetLastError();
1302 if (likely(gle
== 5))
1303 goto already_terminating
;
1304 internal(file_line
, "SuspendThread failed: %u", gle
);
1306 context
.ContextFlags
= CONTEXT_CONTROL
;
1307 if (unlikely(!GetThreadContext(thr
->thread
, &context
))) {
1308 gle
= GetLastError();
1309 if (likely(gle
== 5))
1310 goto already_terminating
;
1311 internal(file_line
, "GetThreadContext failed: %u", gle
);
1313 fn_RtlFreeUserThreadStack(GetCurrentProcess(), thr
->thread
);
1315 already_terminating
:
1316 if (unlikely(!TerminateThread(thr
->thread
, 0))) {
1317 gle
= GetLastError();
1318 if (unlikely(gle
!= 87))
1319 internal(file_line
, "TerminateThread failed: %u", gle
);
1321 unlock_mutex(thr
->terminate_mutex
);
1322 wait_for_event(thr
->thread
);
1323 win32_close_handle(thr
->terminate_mutex
);
1324 } else if (thr
->h
->is_console
|| thr
->h
->is_overlapped
) {
1325 address_lock(thr
->h
, DEPTH_THUNK
);
1326 thr
->need_terminate
= true;
1327 win32_set_event(thr
->data_event
);
1328 win32_set_event(thr
->terminate_event
);
1329 address_unlock(thr
->h
, DEPTH_THUNK
);
1330 wait_for_event(thr
->thread
);
1331 win32_close_handle(thr
->terminate_event
);
1332 if (thr
->h
->is_console
)
1333 mem_free(thr
->line_buffer
);
1337 address_lock(thr
->h
, DEPTH_THUNK
);
1338 thr
->need_terminate
= true;
1339 win32_set_event(thr
->data_event
);
1340 if (unlikely(!fn_CancelIoEx(thr
->h
->h
, NULL
))) {
1341 DWORD gle
= GetLastError();
1342 if (unlikely(gle
!= 6) && unlikely(gle
!= 1168))
1343 internal(file_line
, "CancelIoEx failed: %u", gle
);
1345 address_unlock(thr
->h
, DEPTH_THUNK
);
1346 if (unlikely(!wait_for_event_timeout(thr
->thread
))) {
1350 win32_close_handle(thr
->thread
);
1352 win32_close_handle(thr
->data_event
);
1353 mem_free(thr
->buffer
);
1357 static bool win32_create_read_thread(handle_t h
, ajla_error_t
*err
)
1359 if (unlikely(!h
->rd
))
1360 return win32_create_io_thread(h
, &h
->rd
, win32_read_thread
, err
);
1364 static bool win32_create_write_thread(handle_t h
, ajla_error_t
*err
)
1366 if (unlikely(!h
->wr
))
1367 return win32_create_io_thread(h
, &h
->wr
, win32_write_thread
, err
);
1371 static void win32_close_read_thread(handle_t h
)
1373 struct win32_io_thread
*thr
;
1374 address_lock(h
, DEPTH_THUNK
);
1376 address_unlock(h
, DEPTH_THUNK
);
1381 call(wake_up_wait_list
)(&h
->wait_list
[0], address_get_mutex(h
, DEPTH_THUNK
), true);
1382 win32_terminate_io_thread(thr
);
1385 static ssize_t
os_read_nonblock(handle_t h
, char *buffer
, int size
, ajla_error_t
*err
)
1388 address_lock(h
, DEPTH_THUNK
);
1389 if (unlikely(!win32_create_read_thread(h
, err
))) {
1390 address_unlock(h
, DEPTH_THUNK
);
1393 if (load_relaxed(&h
->rd
->is_packet_console
)) {
1394 store_relaxed(&h
->rd
->is_packet_console
, false);
1395 h
->rd
->packet_is_queued
= false;
1396 win32_set_event(h
->rd
->data_event
);
1398 if (h
->rd
->buffer_len
) {
1399 bool was_full
= h
->rd
->buffer_len
== WIN32_BUFFER_SIZE
;
1400 this_len
= minimum(h
->rd
->buffer_len
, WIN32_BUFFER_SIZE
- h
->rd
->buffer_pos
);
1401 this_len
= minimum(this_len
, size
);
1402 memcpy(buffer
, h
->rd
->buffer
+ h
->rd
->buffer_pos
, this_len
);
1403 h
->rd
->buffer_pos
= (h
->rd
->buffer_pos
+ this_len
) % WIN32_BUFFER_SIZE
;
1404 h
->rd
->buffer_len
-= this_len
;
1406 win32_set_event(h
->rd
->data_event
);
1409 if (unlikely(h
->rd
->err
!= 0)) {
1410 ajla_error_t e
= error_from_win32(EC_SYSCALL
, h
->rd
->err
);
1411 fatal_mayfail(e
, err
, "can't read handle: %s", error_decode(e
));
1412 this_len
= OS_RW_ERROR
;
1415 if (unlikely(h
->rd
->eof
)) {
1419 this_len
= OS_RW_WOULDBLOCK
;
1422 address_unlock(h
, DEPTH_THUNK
);
1426 static ssize_t
os_write_nonblock(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
1430 address_lock(h
, DEPTH_THUNK
);
1431 if (unlikely(!win32_create_write_thread(h
, err
))) {
1432 address_unlock(h
, DEPTH_THUNK
);
1435 if (unlikely(h
->wr
->err
)) {
1436 ajla_error_t e
= error_from_win32(EC_SYSCALL
, h
->wr
->err
);
1437 fatal_mayfail(e
, err
, "can't write handle: %s", error_decode(e
));
1438 this_len
= OS_RW_ERROR
;
1441 if (h
->wr
->buffer_len
< WIN32_BUFFER_SIZE
) {
1442 bool was_empty
= !h
->wr
->buffer_len
;
1443 ptr
= (h
->wr
->buffer_pos
+ h
->wr
->buffer_len
) % WIN32_BUFFER_SIZE
;
1444 this_len
= h
->wr
->buffer_pos
<= ptr
? WIN32_BUFFER_SIZE
- ptr
: h
->wr
->buffer_pos
- ptr
;
1445 this_len
= minimum(this_len
, size
);
1446 memcpy(h
->wr
->buffer
+ ptr
, buffer
, this_len
);
1447 h
->wr
->buffer_len
+= this_len
;
1449 win32_set_event(h
->wr
->data_event
);
1452 this_len
= OS_RW_WOULDBLOCK
;
1455 address_unlock(h
, DEPTH_THUNK
);
1459 static bool win32_setfileptr(handle_t h
, os_off_t off
, DWORD rel
, os_off_t
*result
, ajla_error_t
*err
)
1464 if (unlikely(h
->type
!= FILE_TYPE_DISK
)) {
1465 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to seek on non-disk handle");
1468 high
= off
>> 31 >> 1;
1470 low_ret
= SetFilePointer(h
->h
, off
, &high
, rel
);
1471 gle
= GetLastError();
1472 if (unlikely(gle
!= 0)) {
1473 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
1474 fatal_mayfail(e
, err
, "can't set handle position: %s", error_decode(e
));
1478 *result
= low_ret
+ ((uint64_t)(uint32_t)high
<< 32);
1483 static ssize_t
os_do_rw(handle_t h
, char *buffer
, int size
, bool wr
, os_off_t
*off
, ajla_error_t
*err
)
1487 bool need_lock
= os_threads_initialized
&& (off
|| (h
->flags
& O_APPEND
&& likely(h
->type
== FILE_TYPE_DISK
)));
1489 if (likely(need_lock
))
1490 address_lock(h
, DEPTH_THUNK
);
1493 if (unlikely(!win32_setfileptr(h
, *off
, FILE_BEGIN
, NULL
, err
)))
1494 goto unlock_ret_error
;
1495 } else if (h
->flags
& O_APPEND
&& likely(h
->type
== FILE_TYPE_DISK
)) {
1496 if (unlikely(!win32_setfileptr(h
, 0, FILE_END
, NULL
, err
)))
1497 goto unlock_ret_error
;
1502 b
= ReadFile(h
->h
, buffer
, size
, &result
, NULL
);
1504 b
= WriteFile(h
->h
, buffer
, size
, &result
, NULL
);
1508 DWORD gle
= GetLastError();
1509 if (gle
== ERROR_OPERATION_ABORTED
) {
1515 e
= error_from_win32(EC_SYSCALL
, gle
);
1517 if (likely(need_lock
))
1518 address_unlock(h
, DEPTH_THUNK
);
1519 fatal_mayfail(e
, err
, "can't %s handle: %s", !wr
? "read from" : "write to", error_decode(e
));
1524 if (likely(need_lock
))
1525 address_unlock(h
, DEPTH_THUNK
);
1530 if (likely(need_lock
))
1531 address_unlock(h
, DEPTH_THUNK
);
1536 static ssize_t
os_write_console(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
1541 if (unlikely(!array_init_mayfail(WCHAR
, &r
, &l
, err
)))
1546 unsigned char c
= *buffer
++;
1548 h
->utf8_buffer_size
= 0;
1549 if (unlikely(!array_add_mayfail(WCHAR
, &r
, &l
, c
, NULL
, err
)))
1552 } else if (c
< 0xc0) {
1553 if (unlikely(!h
->utf8_buffer_size
) || unlikely((size_t)h
->utf8_buffer_size
+ 1 >= sizeof(h
->utf8_buffer
)))
1555 h
->utf8_buffer
[h
->utf8_buffer_size
++] = c
;
1556 } else if (c
< 0xf8) {
1557 h
->utf8_buffer
[0] = c
;
1558 h
->utf8_buffer_size
= 1;
1560 h
->utf8_buffer_size
= 0;
1563 h
->utf8_buffer
[h
->utf8_buffer_size
] = 0;
1564 wc
= utf8_to_wchar(h
->utf8_buffer
, &sink
);
1567 h
->utf8_buffer_size
= 0;
1568 for (wl
= 0; wc
[wl
]; wl
++) ;
1569 if (unlikely(!array_add_multiple_mayfail(WCHAR
, &r
, &l
, wc
, wl
, NULL
, err
))) {
1575 if (unlikely(sink
.error_class
== EC_ASYNC
)) {
1577 fatal("can't allocate console buffer: %s", error_decode(sink
));
1586 BOOL b
= WriteConsoleW(h
->h
, r
, l
, &written
, NULL
);
1589 DWORD gle
= GetLastError();
1591 e
= error_from_win32(EC_SYSCALL
, gle
);
1592 fatal_mayfail(e
, err
, "can't write to console: %s", error_decode(e
));
1600 static ssize_t
os_read_socket(handle_t h
, char *buffer
, int size
, ajla_error_t
*err
)
1604 r
= recv(h
->s
, buffer
, size
, 0);
1605 if (unlikely(r
== SOCKET_ERROR
)) {
1606 int er
= WSAGetLastError();
1607 if (likely(er
== WSAEWOULDBLOCK
))
1608 return OS_RW_WOULDBLOCK
;
1609 fatal_mayfail(error_from_win32_socket(er
), err
, "error reading socket");
1615 static ssize_t
os_write_socket(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
1619 r
= send(h
->s
, buffer
, size
, 0);
1620 if (unlikely(r
== SOCKET_ERROR
)) {
1621 int er
= WSAGetLastError();
1622 if (likely(er
== WSAEWOULDBLOCK
))
1623 return OS_RW_WOULDBLOCK
;
1624 fatal_mayfail(error_from_win32_socket(er
), err
, "error writing socket");
1630 ssize_t
os_read(handle_t h
, char *buffer
, int size
, ajla_error_t
*err
)
1632 if (unlikely((h
->flags
& 3) == O_WRONLY
)) {
1633 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to read from write-only handle");
1637 if (handle_is_socket(h
))
1638 return os_read_socket(h
, buffer
, size
, err
);
1639 if (h
->flags
& O_NONBLOCK
)
1640 return os_read_nonblock(h
, buffer
, size
, err
);
1641 return os_do_rw(h
, buffer
, size
, false, NULL
, err
);
1644 ssize_t
os_write(handle_t h
, const char *buffer
, int size
, ajla_error_t
*err
)
1646 if (unlikely((h
->flags
& 3) == O_RDONLY
)) {
1647 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to write to read-only handle");
1651 if (handle_is_socket(h
))
1652 return os_write_socket(h
, buffer
, size
, err
);
1653 if (h
->flags
& O_NONBLOCK
&& !h
->is_console
)
1654 return os_write_nonblock(h
, buffer
, size
, err
);
1655 if (h
->is_console
&& is_winnt())
1656 return os_write_console(h
, buffer
, size
, err
);
1657 return os_do_rw(h
, cast_ptr(char *, buffer
), size
, true, NULL
, err
);
1660 ssize_t
os_pread(handle_t h
, char *buffer
, int size
, os_off_t off
, ajla_error_t
*err
)
1662 if (unlikely((h
->flags
& 3) == O_WRONLY
)) {
1663 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to read from write-only handle");
1667 if (unlikely(handle_is_socket(h
))) {
1668 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "seek operation on socket");
1671 return os_do_rw(h
, buffer
, size
, false, &off
, err
);
1674 ssize_t
os_pwrite(handle_t h
, const char *buffer
, int size
, os_off_t off
, ajla_error_t
*err
)
1676 if (unlikely((h
->flags
& 3) == O_RDONLY
)) {
1677 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to write to read-only handle");
1681 if (unlikely(handle_is_socket(h
))) {
1682 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "seek operation on socket");
1685 return os_do_rw(h
, cast_ptr(char *, buffer
), size
, true, &off
, err
);
1688 bool os_lseek(handle_t h
, unsigned mode
, os_off_t off
, os_off_t
*result
, ajla_error_t
*err
)
1694 if (unlikely(handle_is_socket(h
))) {
1695 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "seek operation on socket");
1699 if (likely(os_threads_initialized
))
1700 address_lock(h
, DEPTH_THUNK
);
1703 case 0: rel
= FILE_BEGIN
; break;
1704 case 1: rel
= FILE_CURRENT
; break;
1705 case 2: rel
= FILE_END
; break;
1706 case 3: ret
= win32_setfileptr(h
, 0, FILE_END
, &len
, err
);
1709 if (unlikely(off
> len
))
1714 case 4: rel
= FILE_END
; off
= 0; break;
1715 default:internal(file_line
, "os_lseek: unsupported mode %u", mode
);
1716 rel
= (ULONG
)-1; break;
1719 ret
= win32_setfileptr(h
, off
, rel
, result
, err
);
1722 if (likely(os_threads_initialized
))
1723 address_unlock(h
, DEPTH_THUNK
);
1728 bool os_ftruncate(handle_t h
, os_off_t size
, ajla_error_t
*err
)
1733 if (unlikely(handle_is_socket(h
))) {
1734 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "ftruncate operation on socket");
1738 if (likely(os_threads_initialized
))
1739 address_lock(h
, DEPTH_THUNK
);
1741 ret
= win32_setfileptr(h
, size
, FILE_BEGIN
, NULL
, err
);
1745 b
= SetEndOfFile(h
->h
);
1747 DWORD gle
= GetLastError();
1748 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
1749 fatal_mayfail(e
, err
, "can't set file size: %s", error_decode(e
));
1757 if (likely(os_threads_initialized
))
1758 address_unlock(h
, DEPTH_THUNK
);
1763 bool os_fallocate(handle_t attr_unused h
, os_off_t attr_unused position
, os_off_t attr_unused size
, ajla_error_t attr_unused
*err
)
1768 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
)
1770 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "clone not supported");
1774 bool os_fsync(handle_t h
, unsigned attr_unused mode
, ajla_error_t
*err
)
1778 if (unlikely(!handle_is_valid(h
)) || unlikely(h
->is_console
))
1781 if (unlikely(handle_is_socket(h
))) {
1782 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "fsync operation on socket");
1786 b
= FlushFileBuffers(h
->h
);
1788 DWORD gle
= GetLastError();
1789 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
1790 fatal_mayfail(e
, err
, "can't flush file: %s", error_decode(e
));
1798 int os_charset(void)
1806 int os_charset_console(void)
1811 return GetConsoleOutputCP();
1814 ssize_t
os_read_console_packet(handle_t h
, struct console_read_packet
*result
, ajla_error_t
*err
)
1818 address_lock(h
, DEPTH_THUNK
);
1819 if (unlikely(!h
->is_console
) || unlikely((h
->flags
& 3) == O_WRONLY
)) {
1820 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to use packet console on non-console");
1821 retval
= OS_RW_ERROR
;
1824 if (GetConsoleMode(h
->h
, &cmode
)) {
1825 if (unlikely(!(cmode
& ENABLE_MOUSE_INPUT
))) {
1826 cmode
|= ENABLE_MOUSE_INPUT
;
1827 SetConsoleMode(h
->h
, cmode
);
1830 if (unlikely(!win32_create_read_thread(h
, err
))) {
1831 address_unlock(h
, DEPTH_THUNK
);
1832 retval
= OS_RW_ERROR
;
1835 if (!load_relaxed(&h
->rd
->is_packet_console
)) {
1836 store_relaxed(&h
->rd
->is_packet_console
, true);
1837 h
->rd
->last_buttons
= 0;
1838 h
->rd
->buffer_len
= 0;
1839 win32_set_event(h
->rd
->data_event
);
1841 if (h
->rd
->packet_is_queued
) {
1842 memcpy(result
, &h
->rd
->packet
, sizeof(struct console_read_packet
));
1843 h
->rd
->packet_is_queued
= false;
1844 win32_set_event(h
->rd
->data_event
);
1847 if (unlikely(h
->rd
->err
!= 0)) {
1848 ajla_error_t e
= error_from_win32(EC_SYSCALL
, h
->rd
->err
);
1849 fatal_mayfail(e
, err
, "can't read console packet: %s", error_decode(e
));
1850 retval
= OS_RW_ERROR
;
1852 retval
= OS_RW_WOULDBLOCK
;
1856 address_unlock(h
, DEPTH_THUNK
);
1860 static atomic_type
unsigned window_offset_x
;
1861 static atomic_type
unsigned window_offset_y
;
1863 bool os_write_console_packet(handle_t h
, struct console_write_packet
*packet
, ajla_error_t
*err
)
1866 unsigned offset_x
= load_relaxed(&window_offset_x
);
1867 unsigned offset_y
= load_relaxed(&window_offset_y
);
1868 bool wnt
= is_winnt();
1869 if (unlikely(!h
->is_console
) || unlikely((h
->flags
& 3) == O_RDONLY
)) {
1870 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "attempting to use packet console on non-console");
1874 if (h
->is_console
&& (h
->flags
& 3) == O_RDONLY
) {
1876 h1
= os_get_std_handle(1);
1877 if (h1
->is_console
) {
1881 h1
= os_get_std_handle(2);
1882 if (h1
->is_console
) {
1891 switch (packet
->type
) {
1899 COORD dwBufferCoord
;
1900 SMALL_RECT lpWriteRegion
;
1902 chr
= mem_alloc_array_mayfail(mem_alloc_mayfail
, CHAR_INFO
*, 0, 0, packet
->u
.c
.n_chars
, sizeof(CHAR_INFO
), err
);
1905 for (i
= 0; i
< packet
->u
.c
.n_chars
; i
++) {
1907 chr
[i
].Char
.UnicodeChar
= packet
->u
.c
.data
[i
* 2];
1909 chr
[i
].Char
.AsciiChar
= packet
->u
.c
.data
[i
* 2];
1910 chr
[i
].Attributes
= packet
->u
.c
.data
[i
* 2 + 1];
1912 dwBufferSize
.X
= packet
->u
.c
.n_chars
;
1914 dwBufferCoord
.X
= 0;
1915 dwBufferCoord
.Y
= 0;
1916 lpWriteRegion
.Left
= packet
->u
.c
.x
+ offset_x
;
1917 lpWriteRegion
.Top
= packet
->u
.c
.y
+ offset_y
;
1918 lpWriteRegion
.Right
= packet
->u
.c
.x
+ packet
->u
.c
.n_chars
- 1 + offset_x
;
1919 lpWriteRegion
.Bottom
= packet
->u
.c
.y
+ offset_y
;
1921 ret
= WriteConsoleOutputW(h
->h
, chr
, dwBufferSize
, dwBufferCoord
, &lpWriteRegion
);
1923 ret
= WriteConsoleOutputA(h
->h
, chr
, dwBufferSize
, dwBufferCoord
, &lpWriteRegion
);
1924 if (unlikely(!ret
)) {
1925 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1926 fatal_mayfail(e
, err
, "can't write to console buffer");
1931 packet
= cast_ptr(struct console_write_packet
*, &packet
->u
.c
.data
[packet
->u
.c
.n_chars
* 2]);
1935 COORD dwCursorPosition
;
1936 dwCursorPosition
.X
= packet
->u
.p
.x
+ offset_x
;
1937 dwCursorPosition
.Y
= packet
->u
.p
.y
+ offset_y
;
1938 ret
= SetConsoleCursorPosition(h
->h
, dwCursorPosition
);
1939 /*if (unlikely(!ret)) {
1940 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1941 fatal_mayfail(e, err, "can't set cursor position");
1944 packet
= cast_ptr(struct console_write_packet
*, &packet
->u
.p
.end
);
1948 CONSOLE_CURSOR_INFO cci
;
1949 ret
= GetConsoleCursorInfo(h
->h
, &cci
);
1950 if (unlikely(!ret
)) {
1951 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1952 fatal_mayfail(e
, err
, "can't get cursor info");
1955 cci
.bVisible
= packet
->u
.v
.v
;
1956 ret
= SetConsoleCursorInfo(h
->h
, &cci
);
1957 if (unlikely(!ret
)) {
1958 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
1959 fatal_mayfail(e
, err
, "can't set cursor info");
1962 packet
= cast_ptr(struct console_write_packet
*, &packet
->u
.v
.end
);
1966 internal(file_line
, "os_write_console_packet: invalid type %d", (int)packet
->type
);
1974 dir_handle_t
os_dir_root(ajla_error_t
*err
)
1978 char *d
= str_dup(" :\\", -1, err
);
1981 drv
= GetLogicalDrives();
1991 dir_handle_t
os_dir_cwd(ajla_error_t
*err
)
1993 DWORD buf_size
, data_size
;
1994 char *buf
= NULL
; /* avoid warning */
1995 WCHAR
*wbuf
= NULL
; /* avoid warning */
1998 buf_size
= GetCurrentDirectoryW(0, NULL
);
2000 buf_size
= GetCurrentDirectoryA(0, NULL
);
2002 if (unlikely(!buf_size
)) {
2003 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
2004 fatal_mayfail(e
, err
, "can't get current directory");
2010 wbuf
= mem_alloc_mayfail(WCHAR
*, buf_size
* sizeof(WCHAR
), err
);
2011 if (unlikely(!wbuf
))
2013 data_size
= GetCurrentDirectoryW(buf_size
, wbuf
);
2015 buf
= mem_alloc_mayfail(char *, buf_size
, err
);
2018 data_size
= GetCurrentDirectoryA(buf_size
, buf
);
2020 if (unlikely(!data_size
)) {
2021 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
2022 mem_free(is_winnt() ? cast_ptr(void *, wbuf
) : cast_ptr(void *, buf
));
2023 fatal_mayfail(e
, err
, "can't get current directory");
2026 if (unlikely(data_size
> buf_size
)) {
2027 mem_free(is_winnt() ? cast_ptr(void *, wbuf
) : cast_ptr(void *, buf
));
2028 buf_size
= data_size
;
2032 buf
= wchar_to_utf8(NULL
, wbuf
, err
);
2038 dir_handle_t
os_dir_open(dir_handle_t dir
, const char *path
, int attr_unused flags
, ajla_error_t
*err
)
2043 joined
= os_join_paths(dir
, path
, true, err
);
2044 if (unlikely(!joined
))
2047 WCHAR
*w
= utf8_to_wchar(joined
, err
);
2052 attr
= GetFileAttributesW(w
);
2053 gle
= GetLastError();
2056 attr
= GetFileAttributesA(joined
);
2057 gle
= GetLastError();
2059 if (unlikely(attr
== INVALID_FILE_ATTRIBUTES
)) {
2060 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
2062 fatal_mayfail(e
, err
, "can't open directory");
2065 if (unlikely(!(attr
& FILE_ATTRIBUTE_DIRECTORY
))) {
2066 ajla_error_t e
= error_ajla_system(EC_SYSCALL
, SYSTEM_ERROR_ENOTDIR
);
2068 fatal_mayfail(e
, err
, "can't open directory");
2074 void os_dir_close(dir_handle_t h
)
2079 char *os_dir_path(dir_handle_t h
, ajla_error_t
*err
)
2081 return str_dup(h
, -1, err
);
2084 static bool add_file_name(char *a
, char ***files
, size_t *n_files
, ajla_error_t
*err
)
2087 if (unlikely(!strcmp(a
, ".")) ||
2088 unlikely(!strcmp(a
, ".."))) {
2092 if (unlikely(!array_add_mayfail(char *, files
, n_files
, a
, &err_ptr
, err
))) {
2100 bool os_dir_read(dir_handle_t h
, char ***files
, size_t *n_files
, ajla_error_t
*err
)
2107 WIN32_FIND_DATAA find_data_a
;
2108 WIN32_FIND_DATAW find_data_w
;
2111 if (unlikely(!array_init_mayfail(char *, files
, n_files
, err
)))
2114 fn
= os_join_paths(h
, "*", false, err
);
2119 WCHAR
*w
= utf8_to_wchar(fn
, err
);
2125 hdir
= FindFirstFileW(w
, &u
.find_data_w
);
2126 gle
= GetLastError();
2129 hdir
= FindFirstFileA(fn
, &u
.find_data_a
);
2130 gle
= GetLastError();
2134 if (unlikely(hdir
== INVALID_HANDLE_VALUE
)) {
2136 if (/*likely(gle == ERROR_FILE_NOT_FOUND) ||*/ likely(gle
== ERROR_NO_MORE_FILES
))
2138 e
= error_from_win32(EC_SYSCALL
, gle
);
2139 fatal_mayfail(e
, err
, "can't read directory");
2145 char *a
= wchar_to_utf8(NULL
, u
.find_data_w
.cFileName
, err
);
2147 goto close_h_ret_false
;
2148 if (unlikely(!add_file_name(a
, files
, n_files
, err
)))
2149 goto close_h_ret_false
;
2151 char *a
= str_dup(u
.find_data_a
.cFileName
, -1, err
);
2153 goto close_h_ret_false
;
2154 if (unlikely(!add_file_name(a
, files
, n_files
, err
)))
2155 goto close_h_ret_false
;
2159 b
= FindNextFileW(hdir
, &u
.find_data_w
);
2161 b
= FindNextFileA(hdir
, &u
.find_data_a
);
2164 DWORD gle
= GetLastError();
2165 if (/*likely(gle == ERROR_FILE_NOT_FOUND) ||*/ likely(gle
== ERROR_NO_MORE_FILES
)) {
2166 if (unlikely(!FindClose(hdir
)))
2167 internal(file_line
, "FindClose failed: %u", GetLastError());
2170 e
= error_from_win32(EC_SYSCALL
, gle
);
2171 fatal_mayfail(e
, err
, "can't read directory");
2172 goto close_h_ret_false
;
2178 if (unlikely(!FindClose(hdir
)))
2179 internal(file_line
, "FindClose failed: %u", GetLastError());
2181 os_dir_free(*files
, *n_files
);
2185 void os_dir_free(char **files
, size_t n_files
)
2188 for (i
= 0; i
< n_files
; i
++)
2193 unsigned os_dev_t_major(dev_t attr_unused dev
)
2198 unsigned os_dev_t_minor(dev_t attr_unused dev
)
2203 ajla_time_t
os_time_t_to_ajla_time(os_time_t tick
)
2205 return tick
/ 10 - (int64_t)116444736 * (int64_t)100000000;
2208 static os_time_t
ajla_time_to_os_time(ajla_time_t a
)
2210 return 10 * a
+ (int64_t)10 * (int64_t)116444736 * (int64_t)100000000;
2213 static uint64_t get_win32_time(const FILETIME
*ft
)
2215 /*debug("get_win32_time: %llx", ((uint64_t)ft->dwHighDateTime << 32) | ft->dwLowDateTime);*/
2216 return ((uint64_t)ft
->dwHighDateTime
<< 32) | ft
->dwLowDateTime
;
2219 static void make_win32_filetime(uint64_t t
, FILETIME
*ft
)
2221 ft
->dwLowDateTime
= t
;
2222 ft
->dwHighDateTime
= t
>> 32;
2225 bool os_fstat(handle_t h
, os_stat_t
*st
, ajla_error_t
*err
)
2227 BY_HANDLE_FILE_INFORMATION info
;
2229 memset(st
, 0, sizeof(*st
));
2232 case FILE_TYPE_DISK
: st
->st_mode
= S_IFREG
; break;
2233 case FILE_TYPE_CHAR
: st
->st_mode
= S_IFCHR
; break;
2234 case FILE_TYPE_PIPE
: st
->st_mode
= S_IFIFO
; break;
2235 default: st
->st_mode
= S_IFCHR
; break;
2238 if (unlikely(!GetFileInformationByHandle(h
->h
, &info
))) {
2239 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
2240 fatal_mayfail(e
, err
, "GetFileInformationByHandle returned an error: %s", error_decode(e
));
2244 st
->st_dev
= info
.dwVolumeSerialNumber
;
2245 st
->st_ino
= ((uint64_t)info
.nFileIndexHigh
<< 32) | info
.nFileIndexLow
;
2246 st
->st_size
= ((uint64_t)info
.nFileSizeHigh
<< 32) | info
.nFileSizeLow
;
2247 st
->st_blocks
= round_up(st
->st_size
, 4096) / 512;
2248 st
->st_blksize
= 4096;
2249 st
->st_nlink
= info
.nNumberOfLinks
;
2250 st
->st_ctime
= get_win32_time(&info
.ftCreationTime
);
2251 st
->st_atime
= get_win32_time(&info
.ftLastAccessTime
);
2252 st
->st_mtime
= get_win32_time(&info
.ftLastWriteTime
);
2254 if (unlikely(info
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
2255 st
->st_mode
|= 0444;
2257 st
->st_mode
|= 0666;
2262 bool os_stat(dir_handle_t dir
, const char *path
, bool attr_unused lnk
, os_stat_t
*st
, ajla_error_t
*err
)
2267 WIN32_FIND_DATAA find_data_a
;
2268 WIN32_FIND_DATAW find_data_w
;
2274 memset(st
, 0, sizeof(*st
));
2276 dh
= os_dir_open(dir
, path
, 0, &sink
);
2277 if (dir_handle_is_valid(dh
)) {
2278 st
->st_mode
= S_IFDIR
| 0777;
2280 handle_t fh
= os_open(dir
, path
, O_RDONLY
, 0, err
);
2281 if (handle_is_valid(fh
)) {
2282 bool succ
= os_fstat(fh
, st
, err
);
2286 st
->st_mode
= S_IFREG
| 0666;
2287 dh
= os_join_paths(dir
, path
, false, err
);
2292 if (unlikely(strchr(dh
, '*') != NULL
) || unlikely(strchr(dh
, '?') != NULL
)) {
2293 ajla_error_t e
= error_ajla_system(EC_SYSCALL
, SYSTEM_ERROR_ENOENT
);
2294 fatal_mayfail(e
, err
, "can't open file '%s': %s", dh
, error_decode(e
));
2300 WCHAR
*w
= utf8_to_wchar(dh
, err
);
2305 hdir
= FindFirstFileW(w
, &u
.find_data_w
);
2306 gle
= GetLastError();
2309 hdir
= FindFirstFileA(dh
, &u
.find_data_a
);
2310 gle
= GetLastError();
2312 if (unlikely(hdir
== INVALID_HANDLE_VALUE
)) {
2313 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
2314 fatal_mayfail(e
, err
, "can't open file '%s': %s", dh
, error_decode(e
));
2322 st
->st_size
= ((uint64_t)u
.find_data_w
.nFileSizeHigh
<< 32) | u
.find_data_a
.nFileSizeLow
;
2323 st
->st_ctime
= get_win32_time(&u
.find_data_w
.ftCreationTime
);
2324 st
->st_atime
= get_win32_time(&u
.find_data_w
.ftLastAccessTime
);
2325 st
->st_mtime
= get_win32_time(&u
.find_data_w
.ftLastWriteTime
);
2326 if (unlikely(u
.find_data_w
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
2327 st
->st_mode
&= ~0222;
2329 st
->st_size
= ((uint64_t)u
.find_data_a
.nFileSizeHigh
<< 32) | u
.find_data_a
.nFileSizeLow
;
2330 st
->st_ctime
= get_win32_time(&u
.find_data_a
.ftCreationTime
);
2331 st
->st_atime
= get_win32_time(&u
.find_data_a
.ftLastAccessTime
);
2332 st
->st_mtime
= get_win32_time(&u
.find_data_a
.ftLastWriteTime
);
2333 if (unlikely(u
.find_data_a
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
2334 st
->st_mode
&= ~0222;
2336 st
->st_blocks
= round_up(st
->st_size
, 4096) / 512;
2337 st
->st_blksize
= 4096;
2339 if (unlikely(!FindClose(hdir
)))
2340 internal(file_line
, "FindClose failed: %u", GetLastError());
2346 bool os_fstatvfs(handle_t h
, os_statvfs_t
*st
, ajla_error_t
*err
)
2348 if (unlikely(!h
->file_name
[0])) {
2349 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "statvfs not supported");
2352 return os_dstatvfs(h
->file_name
, st
, err
);
2355 bool os_dstatvfs(dir_handle_t dir
, os_statvfs_t
*st
, ajla_error_t
*err
)
2361 DWORD spc
, bps
, freecl
, totalcl
;
2363 ULARGE_INTEGER availb
, totalb
, freeb
;
2364 availb
.QuadPart
= 0; /* avoid warning */
2365 totalb
.QuadPart
= 0; /* avoid warning */
2366 freeb
.QuadPart
= 0; /* avoid warning */
2367 if ((dir
[0] == '/' || dir
[0] == '\\') && (dir
[1] == '/' || dir
[1] == '\\')) {
2369 len
+= strcspn(dir
+ 2, "/\\");
2372 len
+= strcspn(dir
+ len
, "/\\");
2376 } else if ((dir
[0] & 0xDF) >= 'A' && (dir
[0] & 0xDF) <= 'Z' && dir
[1] == ':' && (dir
[2] == '/' || dir
[2] == '\\')) {
2379 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "statvfs not on this directory");
2382 disk
= str_dup(dir
, len
, err
);
2383 if (unlikely(!disk
))
2386 WCHAR
*w
= utf8_to_wchar(disk
, err
);
2392 if (fn_GetDiskFreeSpaceExW
)
2393 b2
= fn_GetDiskFreeSpaceExW(w
, &availb
, &totalb
, &freeb
);
2394 b
= GetDiskFreeSpaceW(w
, &spc
, &bps
, &freecl
, &totalcl
);
2395 gle
= GetLastError();
2399 if (fn_GetDiskFreeSpaceExA
)
2400 b2
= fn_GetDiskFreeSpaceExA(disk
, &availb
, &totalb
, &freeb
);
2401 b
= GetDiskFreeSpaceA(disk
, &spc
, &bps
, &freecl
, &totalcl
);
2402 gle
= GetLastError();
2405 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
2406 fatal_mayfail(e
, err
, "can't get disk '%s' free space: %s", disk
, error_decode(e
));
2410 memset(st
, 0, sizeof(os_statvfs_t
));
2411 st
->f_bsize
= bps
* spc
;
2413 st
->f_blocks
= (uint64_t)totalcl
* spc
;
2414 st
->f_bfree
= st
->f_bavail
= (uint64_t)freecl
* spc
;
2415 st
->f_fsid
= disk
[0];
2416 st
->f_namemax
= 255;
2418 st
->f_blocks
= totalb
.QuadPart
/ st
->f_frsize
;
2419 st
->f_bfree
= freeb
.QuadPart
/ st
->f_frsize
;
2420 st
->f_bavail
= availb
.QuadPart
/ st
->f_frsize
;
2426 char *os_readlink(dir_handle_t attr_unused dir
, const char attr_unused
*path
, ajla_error_t
*err
)
2428 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "readlink not supported");
2432 bool os_dir_action(dir_handle_t dir
, const char *path
, int action
, int attr_unused mode
, ajla_time_t dev_major
, ajla_time_t dev_minor
, const char attr_unused
*syml
, ajla_error_t
*err
)
2438 WCHAR
*joined_w
= NULL
;
2439 bool allow_trailing_slash
= action
== IO_Action_Rm_Dir
|| action
== IO_Action_Mk_Dir
;
2441 joined
= os_join_paths(dir
, path
, allow_trailing_slash
, err
);
2442 if (unlikely(!joined
))
2446 joined_w
= utf8_to_wchar(joined
, err
);
2447 if (unlikely(!joined_w
)) {
2456 b
= DeleteFileW(joined_w
);
2458 b
= DeleteFileA(joined
);
2459 gle
= GetLastError();
2461 case IO_Action_Rm_Dir
:
2463 b
= RemoveDirectoryW(joined_w
);
2465 b
= RemoveDirectoryA(joined
);
2466 gle
= GetLastError();
2468 case IO_Action_Mk_Dir
:
2470 b
= CreateDirectoryW(joined_w
, NULL
);
2472 b
= CreateDirectoryA(joined
, NULL
);
2473 gle
= GetLastError();
2474 if (gle
== ERROR_ACCESS_DENIED
) {
2475 if ((joined
[0] & 0xdf) >= 'A' && (joined
[0] & 0xdf) <= 'Z' && joined
[1] == ':' && joined
[2] && !joined
[2 + strspn(joined
+ 2, "\\/")])
2476 gle
= ERROR_ALREADY_EXISTS
;
2479 case IO_Action_Mk_Pipe
:
2480 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mkpipe not supported");
2482 case IO_Action_Mk_Socket
:
2483 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mksocket not supported");
2485 case IO_Action_Mk_CharDev
:
2486 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mkchardev not supported");
2488 case IO_Action_Mk_BlockDev
:
2489 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mkblockdev not supported");
2491 case IO_Action_Mk_SymLink
:
2492 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mksymlink not supported");
2494 case IO_Action_ChMod
:
2495 case IO_Action_ChOwn
:
2496 case IO_Action_LChOwn
:
2498 b
= GetFileAttributesW(joined_w
) != INVALID_FILE_ATTRIBUTES
;
2500 b
= GetFileAttributesA(joined
) != INVALID_FILE_ATTRIBUTES
;
2501 gle
= GetLastError();
2503 case IO_Action_UTime
:
2504 case IO_Action_LUTime
: {
2506 FILETIME modft
, accft
;
2507 make_win32_filetime(ajla_time_to_os_time(dev_major
), &modft
);
2508 make_win32_filetime(ajla_time_to_os_time(dev_minor
), &accft
);
2510 hfile
= CreateFileW(joined_w
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, 0, NULL
);
2512 hfile
= CreateFileA(joined
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, 0, NULL
);
2513 if (unlikely(hfile
== INVALID_HANDLE_VALUE
)) {
2514 gle
= GetLastError();
2517 b
= SetFileTime(hfile
, NULL
, &accft
, &modft
);
2518 gle
= GetLastError();
2519 win32_close_handle(hfile
);
2523 internal(file_line
, "os_dir_action: invalid action %d", action
);
2529 e
= error_from_win32(EC_SYSCALL
, gle
);
2530 fatal_mayfail(e
, err
, "can't perform action %d on '%s': %s", action
, joined
, error_decode(e
));
2543 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
)
2548 char *dest_joined
= NULL
, *src_joined
= NULL
;
2549 WCHAR
*dest_joined_w
= NULL
, *src_joined_w
= NULL
;
2551 dest_joined
= os_join_paths(dest_dir
, dest_path
, false, err
);
2552 if (unlikely(!dest_joined
))
2555 src_joined
= os_join_paths(src_dir
, src_path
, false, err
);
2556 if (unlikely(!src_joined
))
2559 dest_joined_w
= utf8_to_wchar(dest_joined
, err
);
2560 if (unlikely(!dest_joined_w
)) {
2561 mem_free(dest_joined
);
2564 src_joined_w
= utf8_to_wchar(src_joined
, err
);
2565 if (unlikely(!src_joined_w
)) {
2566 mem_free(src_joined
);
2572 case IO_Action_Mk_Link
:
2573 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mklink not supported");
2575 case IO_Action_Rename
:
2576 if (dest_joined_w
) {
2577 if (unlikely(!fn_MoveFileExW
)) {
2579 if (unlikely(GetFileAttributesW(src_joined_w
) == INVALID_FILE_ATTRIBUTES
)) {
2580 gle
= GetLastError();
2584 DeleteFileW(dest_joined_w
);
2585 b
= MoveFileW(src_joined_w
, dest_joined_w
);
2586 gle
= GetLastError();
2588 b
= fn_MoveFileExW(src_joined_w
, dest_joined_w
, MOVEFILE_REPLACE_EXISTING
);
2589 gle
= GetLastError();
2590 if (unlikely(!b
) && gle
== ERROR_CALL_NOT_IMPLEMENTED
)
2594 if (unlikely(!fn_MoveFileExA
)) {
2596 if (unlikely(GetFileAttributesA(src_joined
) == INVALID_FILE_ATTRIBUTES
)) {
2597 gle
= GetLastError();
2601 DeleteFileA(dest_joined
);
2602 b
= MoveFileA(src_joined
, dest_joined
);
2603 gle
= GetLastError();
2605 b
= fn_MoveFileExA(src_joined
, dest_joined
, MOVEFILE_REPLACE_EXISTING
);
2606 gle
= GetLastError();
2607 if (unlikely(!b
) && gle
== ERROR_CALL_NOT_IMPLEMENTED
)
2613 internal(file_line
, "os_dir2_action: invalid action %d", action
);
2618 e
= error_from_win32(EC_SYSCALL
, gle
);
2619 fatal_mayfail(e
, err
, "can't perform action %d on '%s' and '%s': %s", action
, src_joined
, dest_joined
, error_decode(e
));
2627 mem_free(dest_joined
);
2629 mem_free(dest_joined_w
);
2631 mem_free(src_joined
);
2633 mem_free(src_joined_w
);
2637 bool os_drives(char **drives
, size_t *drives_l
, ajla_error_t
*err
)
2639 uint32_t mask
= GetLogicalDrives();
2640 return os_drives_bitmap(mask
, drives
, drives_l
, err
);
2644 bool os_tcgetattr(handle_t h
, os_termios_t
*t
, ajla_error_t attr_unused
*err
)
2646 t
->tc_flags
= h
->tc_flags
;
2650 bool os_tcsetattr(handle_t h
, const os_termios_t
*t
, ajla_error_t attr_unused
*err
)
2652 h
->tc_flags
= t
->tc_flags
;
2653 if (h
->is_console
) {
2654 if (t
->tc_flags
& IO_Stty_Flag_Nosignal
)
2655 SetConsoleCtrlHandler(NULL
, TRUE
);
2657 SetConsoleCtrlHandler(NULL
, FALSE
);
2662 void os_tcflags(os_termios_t
*t
, int flags
)
2664 t
->tc_flags
= flags
;
2667 bool os_tty_size(handle_t h
, int *nx
, int *ny
, int *ox
, int *oy
, ajla_error_t
*err
)
2669 CONSOLE_SCREEN_BUFFER_INFO csbi
;
2671 if (h
->is_console
&& (h
->flags
& 3) == O_RDONLY
) {
2673 h1
= os_get_std_handle(1);
2674 if (h1
->is_console
) {
2678 h1
= os_get_std_handle(2);
2679 if (h1
->is_console
) {
2686 if (!GetConsoleScreenBufferInfo(h
->h
, &csbi
)) {
2687 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
2688 fatal_mayfail(e
, err
, "GetConsoleScreenBufefrInfo failed: %s", error_decode(e
));
2692 *nx
= csbi
.srWindow
.Right
- csbi
.srWindow
.Left
+ 1;
2693 *ny
= csbi
.srWindow
.Bottom
- csbi
.srWindow
.Top
+ 1;
2694 *ox
= csbi
.srWindow
.Left
;
2695 *oy
= csbi
.srWindow
.Top
;
2697 store_relaxed(&window_offset_x
, csbi
.srWindow
.Left
);
2698 store_relaxed(&window_offset_y
, csbi
.srWindow
.Top
);
2704 const char *os_get_flavor(void)
2709 void os_get_uname(os_utsname_t
*un
)
2711 OSVERSIONINFOA osva
;
2712 OSVERSIONINFOW osvw
;
2713 unsigned platform
, major
, minor
, build
;
2715 memset(un
, 0, sizeof(os_utsname_t
));
2716 strcpy(un
->sysname
, "Windows");
2718 if (fn_RtlGetVersion
) {
2719 memset(&osvw
, 0, sizeof osvw
);
2720 osvw
.dwOSVersionInfoSize
= sizeof osvw
;
2721 if (unlikely(fn_RtlGetVersion(&osvw
)))
2722 fatal("RtlGetVersion failed");
2724 platform
= osvw
.dwPlatformId
;
2725 major
= osvw
.dwMajorVersion
;
2726 minor
= osvw
.dwMinorVersion
;
2727 build
= osvw
.dwBuildNumber
;
2729 memset(&osva
, 0, sizeof osva
);
2730 osva
.dwOSVersionInfoSize
= sizeof osva
;
2731 if (unlikely(!GetVersionExA(&osva
)))
2732 fatal("GetVersionExA failed: %u", GetLastError());
2734 platform
= osva
.dwPlatformId
;
2735 major
= osva
.dwMajorVersion
;
2736 minor
= osva
.dwMinorVersion
;
2737 build
= osva
.dwBuildNumber
;
2741 case VER_PLATFORM_WIN32s
:
2742 sprintf(un
->release
, "%u.%u", major
, minor
);
2744 case VER_PLATFORM_WIN32_WINDOWS
:
2747 strcpy(un
->release
, "95");
2749 strcpy(un
->release
, "95 OSR2");
2750 } else if (minor
== 10) {
2752 strcpy(un
->release
, "98");
2754 strcpy(un
->release
, "98 SE");
2755 } else if (minor
== 90) {
2756 strcpy(un
->release
, "ME");
2758 sprintf(un
->release
, "%u.%u", major
, minor
);
2761 case VER_PLATFORM_WIN32_NT
:
2762 if (major
== 5 && minor
== 0) {
2763 strcpy(un
->release
, "2000");
2764 } else if (major
== 5 && minor
== 1) {
2765 strcpy(un
->release
, "XP");
2766 } else if (major
== 5 && minor
== 2) {
2767 strcpy(un
->release
, "Server 2003");
2768 } else if (major
== 6 && minor
== 0) {
2769 strcpy(un
->release
, "Vista");
2770 } else if (major
== 6 && minor
== 1) {
2771 strcpy(un
->release
, "7");
2772 } else if (major
== 6 && minor
== 2) {
2773 strcpy(un
->release
, "8");
2774 } else if (major
== 6 && minor
== 3) {
2775 strcpy(un
->release
, "8.1");
2776 } else if (major
== 10 && minor
== 0) {
2778 strcpy(un
->release
, "10");
2780 strcpy(un
->release
, "11");
2782 sprintf(un
->release
, "NT %u.%u", major
, minor
);
2787 if (fn_RtlGetVersion
) {
2789 char *u
= wchar_to_utf8(NULL
, osvw
.szCSDVersion
, &sink
);
2791 strncpy(un
->version
, u
, sizeof un
->version
- 1);
2795 strncpy(un
->version
, osva
.szCSDVersion
, sizeof un
->version
- 1);
2799 strcpy(un
->machine
, ARCH_NAME
);
2803 char *os_get_host_name(ajla_error_t
*err
)
2805 if (fn_GetNetworkParams
) {
2808 dw
= fn_GetNetworkParams(NULL
, &buf_len
);
2809 if (dw
== ERROR_BUFFER_OVERFLOW
) {
2812 buffer
= mem_alloc_mayfail(char *, buf_len
, err
);
2813 if (unlikely(!buffer
))
2815 dw
= fn_GetNetworkParams(buffer
, &buf_len
);
2816 if (likely(dw
== ERROR_SUCCESS
))
2819 if (dw
== ERROR_BUFFER_OVERFLOW
)
2823 return str_dup("", -1, err
);
2827 static char *os_path_to_exe
;
2829 static void os_init_path_to_exe(void)
2836 WCHAR
*w
= mem_alloc(WCHAR
*, s
* sizeof(WCHAR
));
2837 r
= GetModuleFileNameW(NULL
, w
, s
);
2839 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
2840 fatal("GetModuleFileNameW returned an error: %s", error_decode(e
));
2846 fatal("GetModuleFileNameW overflow");
2849 os_path_to_exe
= wchar_to_utf8(NULL
, w
, NULL
);
2852 os_path_to_exe
= mem_alloc(char *, s
);
2853 r
= GetModuleFileNameA(NULL
, os_path_to_exe
, s
);
2855 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
2856 fatal("GetModuleFileNameA returned an error: %s", error_decode(e
));
2859 mem_free(os_path_to_exe
);
2862 fatal("GetModuleFileNameA overflow");
2868 for (i
= 0; os_path_to_exe
[i
]; i
++)
2869 if (os_is_path_separator(os_path_to_exe
[i
]))
2871 os_path_to_exe
[j
] = 0;
2874 const char *os_get_path_to_exe(void)
2876 return os_path_to_exe
;
2880 ajla_time_t
os_time_real(void)
2883 GetSystemTimeAsFileTime(&ft
);
2884 return os_time_t_to_ajla_time(get_win32_time(&ft
));
2887 static mutex_t tick_mutex
;
2888 static DWORD tick_last
;
2889 static DWORD tick_high
;
2891 ajla_time_t
os_time_monotonic(void)
2897 if (likely(fn_QueryPerformanceCounter
!= NULL
)) {
2899 if (likely(fn_QueryPerformanceCounter(&li
))) {
2900 int64_t c
= li
.LowPart
+ ((uint64_t)li
.HighPart
<< 32);
2901 c
= (long double)c
* perf_multiplier
;
2907 if (likely(fn_GetTickCount64
!= NULL
)) {
2908 ret
= fn_GetTickCount64();
2910 if (likely(os_threads_initialized
))
2911 mutex_lock(&tick_mutex
);
2913 if (unlikely(t
< tick_last
))
2916 ret
= ((uint64_t)tick_high
<< 32) | t
;
2917 if (likely(os_threads_initialized
))
2918 mutex_unlock(&tick_mutex
);
2924 static int get_socket_error(handle_t h
)
2928 socklen_t e_l
= sizeof(int);
2930 r
= getsockopt(h
->s
, SOL_SOCKET
, SO_ERROR
, cast_ptr(char *, &e
), &e_l
);
2931 if (unlikely(r
== SOCKET_ERROR
)) {
2932 int er
= WSAGetLastError();
2933 internal(file_line
, "getsockopt returned an error: %d", er
);
2939 void iomux_never(mutex_t
**mutex_to_lock
, struct list
*list_entry
)
2941 *mutex_to_lock
= address_get_mutex(NULL
, DEPTH_THUNK
);
2942 list_init(list_entry
);
2945 static void win32_notify(void);
2947 void iomux_register_wait(handle_t h
, bool wr
, mutex_t
**mutex_to_lock
, struct list
*list_entry
)
2949 struct win32_io_thread
*thr
;
2950 address_lock(h
, DEPTH_THUNK
);
2951 *mutex_to_lock
= address_get_mutex(h
, DEPTH_THUNK
);
2952 list_add(&h
->wait_list
[wr
], list_entry
);
2953 if (handle_is_socket(h
)) {
2954 struct list
*socket_entry
= &h
->socket_entry
[wr
];
2955 address_unlock(h
, DEPTH_THUNK
);
2956 mutex_lock(&socket_list_mutex
);
2957 if (socket_entry
->next
== NULL
)
2958 list_add(&socket_list
[(int)wr
], socket_entry
);
2959 mutex_unlock(&socket_list_mutex
);
2963 thr
= !wr
? h
->rd
: h
->wr
;
2965 if (unlikely(thr
->buffer_len
!= 0) || unlikely(thr
->packet_is_queued
) || unlikely(thr
->err
!= 0) || unlikely(thr
->eof
))
2968 if (unlikely(thr
->buffer_len
!= WIN32_BUFFER_SIZE
) || unlikely(thr
->err
!= 0)) {
2972 address_unlock(h
, DEPTH_THUNK
);
2976 call(wake_up_wait_list
)(&h
->wait_list
[wr
], address_get_mutex(h
, DEPTH_THUNK
), true);
2979 bool iomux_test_handle(handle_t h
, bool wr
)
2981 if (handle_is_socket(h
)) {
2985 if (h
->connect_in_progress
&& likely(wr
)) {
2986 int e
= get_socket_error(h
);
2991 fd
.fd_array
[0] = h
->s
;
2995 r
= select(FD_SETSIZE
, cast_ptr(fd_set
*, &fd
), NULL
, NULL
, &tv
);
2997 r
= select(FD_SETSIZE
, NULL
, cast_ptr(fd_set
*, &fd
), NULL
, &tv
);
2998 if (unlikely(r
== SOCKET_ERROR
))
2999 internal(file_line
, "select returned an error: %d", WSAGetLastError());
3003 * os_read/os_write is non-blocking even for standard handles,
3004 * so we don't need this function
3010 #define HANDLES_PER_THREAD (MAXIMUM_WAIT_OBJECTS - 1)
3012 struct monitor_handle
{
3014 void (*wake_up
)(void *cookie
);
3015 void (*cls
)(HANDLE h
);
3020 struct monitor_thread
{
3026 struct monitor_handle handles
[HANDLES_PER_THREAD
];
3029 static mutex_t monitor_mutex
;
3030 static struct list monotor_threads
;
3032 static inline void monitor_lock(void)
3034 mutex_lock(&monitor_mutex
);
3037 static inline void monitor_unlock(void)
3039 mutex_unlock(&monitor_mutex
);
3042 thread_function_decl(monitor_thread
,
3043 struct monitor_thread
*mt
= arg
;
3044 HANDLE handles
[HANDLES_PER_THREAD
+ 1];
3051 int n_handles
= mt
->n_handles
;
3052 for (i
= j
= 0; i
< n_handles
; i
++, j
++) {
3053 if (mt
->handles
[i
].needs_close
) {
3054 mt
->handles
[i
].cls(mt
->handles
[i
].h
);
3059 mt
->handles
[j
] = mt
->handles
[i
];
3060 handles
[j
] = mt
->handles
[j
].h
;
3062 handles
[j
] = mt
->wake_up
;
3066 r
= WaitForMultipleObjects(j
+ 1, handles
, FALSE
, INFINITE
);
3067 if (unlikely(r
== WAIT_FAILED
))
3068 internal(file_line
, "WaitForMultipleObjects failed: %u", GetLastError());
3070 if (r
>= WAIT_OBJECT_0
+ zero
&& likely(r
< WAIT_OBJECT_0
+ j
)) {
3071 i
= r
- WAIT_OBJECT_0
;
3072 if (i
< mt
->n_handles
&& !mt
->handles
[i
].needs_close
) {
3073 void (*wake_up
)(void *cookie
) = mt
->handles
[i
].wake_up
;
3074 void *cookie
= mt
->handles
[i
].cookie
;
3075 memmove(&mt
->handles
[i
], &mt
->handles
[i
+ 1], (mt
->n_handles
- (i
+ 1)) * sizeof(struct monitor_handle
));
3085 static bool monitor_handle(HANDLE h
, void (*wake_up
)(void *cookie
), void (*cls
)(HANDLE h
), void *cookie
, ajla_error_t
*err
)
3088 struct monitor_thread
*mt
;
3093 list_for_each(l
, &monotor_threads
) {
3094 mt
= get_struct(l
, struct monitor_thread
, entry
);
3096 if (likely(n
< HANDLES_PER_THREAD
)) {
3097 if (unlikely(l
!= monotor_threads
.next
)) {
3098 list_del(&mt
->entry
);
3099 list_add(&monotor_threads
, &mt
->entry
);
3105 mt
= mem_calloc_mayfail(struct monitor_thread
*, sizeof(struct monitor_thread
), err
);
3108 mt
->wake_up
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
3110 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
3111 fatal_mayfail(e
, err
, "can't create event: %s", error_decode(e
));
3114 if (unlikely(!thread_spawn(&mt
->thread
, monitor_thread
, mt
, PRIORITY_IO
, err
))) {
3118 list_add(&monotor_threads
, &mt
->entry
);
3121 mt
->handles
[n
].h
= h
;
3122 mt
->handles
[n
].wake_up
= wake_up
;
3123 mt
->handles
[n
].cls
= cls
;
3124 mt
->handles
[n
].cookie
= cookie
;
3125 mt
->handles
[n
].needs_close
= false;
3126 mt
->n_handles
= n
+ 1;
3127 win32_set_event(mt
->wake_up
);
3136 win32_close_handle(mt
->wake_up
);
3142 static void monitor_close(HANDLE h
)
3145 struct monitor_thread
*mt
;
3148 list_for_each(l
, &monotor_threads
) {
3149 mt
= get_struct(l
, struct monitor_thread
, entry
);
3150 for (n
= 0; n
< mt
->n_handles
; n
++) {
3151 if (h
== mt
->handles
[n
].h
)
3158 mt
->handles
[n
].needs_close
= true;
3160 win32_set_event(mt
->wake_up
);
3163 static void monitor_init(void)
3165 list_init(&monotor_threads
);
3166 mutex_init(&monitor_mutex
);
3169 static void monitor_done(void)
3172 while (!list_is_empty(&monotor_threads
)) {
3173 struct monitor_thread
*mt
= get_struct(monotor_threads
.next
, struct monitor_thread
, entry
);
3174 mt
->terminate
= true;
3175 win32_set_event(mt
->wake_up
);
3177 thread_join(&mt
->thread
);
3179 if (unlikely(mt
->n_handles
))
3180 internal(file_line
, "monitor_done: %d handles were leaked", mt
->n_handles
);
3181 list_del(&mt
->entry
);
3182 win32_close_handle(mt
->wake_up
);
3186 mutex_done(&monitor_mutex
);
3190 struct proc_handle
{
3191 HANDLE process_handle
;
3194 struct list wait_list
;
3197 static void proc_wait_end(void *ph_
)
3199 struct proc_handle
*ph
= ph_
;
3202 if (unlikely(!GetExitCodeProcess(ph
->process_handle
, &ph
->exit_code
)))
3203 internal(file_line
, "GetExitCodeProcess failed: %u", GetLastError());
3204 win32_close_handle(ph
->process_handle
);
3205 call(wake_up_wait_list
)(&ph
->wait_list
, &monitor_mutex
, true);
3208 static bool proc_addstr(char **ptr
, size_t *len
, const char *str
, bool cvt_slashes
, ajla_error_t
*err
)
3213 if (unlikely(!array_add_mayfail(char, ptr
, len
, ' ', NULL
, err
)))
3215 quote
= !str
[0] || str
[strcspn(str
, " \t")];
3216 if (unlikely(quote
)) {
3217 if (unlikely(!array_add_mayfail(char, ptr
, len
, '"', NULL
, err
)))
3221 for (i
= 0; str
[i
]; i
++) {
3223 if (cvt_slashes
&& c
== '/')
3227 } else if (c
== '"') {
3228 for (j
= 0; j
<= bs
; j
++)
3229 if (unlikely(!array_add_mayfail(char, ptr
, len
, '\\', NULL
, err
)))
3235 if (unlikely(!array_add_mayfail(char, ptr
, len
, c
, NULL
, err
)))
3238 if (unlikely(quote
)) {
3239 for (j
= 0; j
< bs
; j
++)
3240 if (unlikely(!array_add_mayfail(char, ptr
, len
, '\\', NULL
, err
)))
3242 if (unlikely(!array_add_mayfail(char, ptr
, len
, '"', NULL
, err
)))
3248 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
)
3251 struct proc_handle
*ph
;
3256 DWORD dwCreationFlags
= 0;
3257 PROCESS_INFORMATION pi
;
3265 path_cpy
= str_dup(path
, -1, err
);
3266 if (unlikely(!path_cpy
))
3268 for (i
= 0; path_cpy
[i
]; i
++)
3269 if (path_cpy
[i
] == '/')
3272 ph
= mem_calloc_mayfail(struct proc_handle
*, sizeof(struct proc_handle
), err
);
3276 list_init(&ph
->wait_list
);
3279 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "empty arguments in spawn");
3282 if (unlikely(!array_init_mayfail(char, &ptr
, &len
, err
)))
3284 for (a
= args
; *a
; a
++) {
3285 if (!proc_addstr(&ptr
, &len
, *a
, a
== args
, err
))
3288 if (unlikely(!array_add_mayfail(char, &ptr
, &len
, 0, NULL
, err
)))
3292 memset(&u
.sti_w
, 0, sizeof u
.sti_w
);
3293 u
.sti_w
.cb
= sizeof u
.sti_w
;
3294 u
.sti_w
.dwFlags
= STARTF_USESTDHANDLES
;
3295 if (fn_GetEnvironmentStringsW
&& fn_FreeEnvironmentStringsW
) {
3296 dwCreationFlags
|= CREATE_UNICODE_ENVIRONMENT
;
3299 memset(&u
.sti_a
, 0, sizeof u
.sti_a
);
3300 u
.sti_a
.cb
= sizeof u
.sti_a
;
3301 u
.sti_a
.dwFlags
= STARTF_USESTDHANDLES
;
3303 for (i
= 0; i
< n_handles
; i
++) {
3305 if (target
[i
] == 0) {
3306 t
= is_winnt() ? &u
.sti_w
.hStdInput
: &u
.sti_a
.hStdInput
;
3307 } else if (target
[i
] == 1) {
3308 t
= is_winnt() ? &u
.sti_w
.hStdOutput
: &u
.sti_a
.hStdOutput
;
3309 } else if (likely(target
[i
] == 2)) {
3310 t
= is_winnt() ? &u
.sti_w
.hStdError
: &u
.sti_a
.hStdError
;
3312 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "only the first three handles can be redirected");
3315 if (unlikely(handle_is_socket(source
[i
]))) {
3316 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "socket can't be inherited by subprocesses on Windows");
3319 if (unlikely(*t
!= NULL
)) {
3320 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "redirecting a handle multiple times");
3323 win32_close_read_thread(source
[i
]);
3324 if (unlikely(!DuplicateHandle(GetCurrentProcess(), source
[i
]->h
, GetCurrentProcess(), t
, 0, TRUE
, DUPLICATE_SAME_ACCESS
))) {
3325 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
3326 fatal_mayfail(e
, err
, "can't duplicate handle: %s", error_decode(e
));
3331 WCHAR
*path_cpy_w
, *ptr_w
, *wd_w
;
3332 path_cpy_w
= utf8_to_wchar(path_cpy
, err
);
3333 if (unlikely(!path_cpy_w
)) {
3336 ptr_w
= utf8_to_wchar(ptr
, err
);
3337 if (unlikely(!ptr_w
)) {
3338 mem_free(path_cpy_w
);
3341 wd_w
= utf8_to_wchar(wd
, err
);
3342 if (unlikely(!wd_w
)) {
3343 mem_free(path_cpy_w
);
3347 if (dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
) {
3351 if (unlikely(!array_init_mayfail(WCHAR
, &uni_env
, &uni_env_l
, err
))) {
3352 mem_free(path_cpy_w
);
3361 WCHAR
*wc
= utf8_to_wchar(e
, &sink
);
3363 if (unlikely(!wc
)) {
3364 if (sink
.error_class
== EC_ASYNC
) {
3366 fatal("can't allocate environment memory: %s", error_decode(sink
));
3369 mem_free(path_cpy_w
);
3376 for (i
= 0; wc
[i
]; i
++) ;
3377 if (unlikely(!array_add_multiple_mayfail(WCHAR
, &uni_env
, &uni_env_l
, wc
, i
+ 1, NULL
, err
))) {
3379 mem_free(path_cpy_w
);
3386 if (unlikely(!array_add_mayfail(WCHAR
, &uni_env
, &uni_env_l
, 0, NULL
, err
))) {
3387 mem_free(path_cpy_w
);
3392 b
= CreateProcessW(path_cpy_w
, ptr_w
, NULL
, NULL
, TRUE
, dwCreationFlags
, uni_env
, wd_w
, &u
.sti_w
, &pi
);
3393 gle
= GetLastError();
3396 b
= CreateProcessW(path_cpy_w
, ptr_w
, NULL
, NULL
, TRUE
, dwCreationFlags
, envc
, wd_w
, &u
.sti_w
, &pi
);
3397 gle
= GetLastError();
3399 mem_free(path_cpy_w
);
3403 b
= CreateProcessA(path_cpy
, ptr
, NULL
, NULL
, TRUE
, dwCreationFlags
, envc
, wd
, &u
.sti_a
, &pi
);
3404 gle
= GetLastError();
3407 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
3408 fatal_mayfail(e
, err
, "can't create process(%s): %s", path
, error_decode(e
));
3411 ph
->process_handle
= pi
.hProcess
;
3412 win32_close_handle(pi
.hThread
);
3414 if (unlikely(!monitor_handle(ph
->process_handle
, proc_wait_end
, win32_close_handle
, ph
, err
))) {
3418 for (i
= 0; i
< 3; i
++) {
3421 t
= is_winnt() ? &u
.sti_w
.hStdInput
: &u
.sti_a
.hStdInput
;
3423 t
= is_winnt() ? &u
.sti_w
.hStdOutput
: &u
.sti_a
.hStdOutput
;
3425 t
= is_winnt() ? &u
.sti_w
.hStdError
: &u
.sti_a
.hStdError
;
3427 win32_close_handle(*t
);
3434 for (i
= 0; i
< 3; i
++) {
3437 t
= is_winnt() ? &u
.sti_w
.hStdInput
: &u
.sti_a
.hStdInput
;
3439 t
= is_winnt() ? &u
.sti_w
.hStdOutput
: &u
.sti_a
.hStdOutput
;
3441 t
= is_winnt() ? &u
.sti_w
.hStdError
: &u
.sti_a
.hStdError
;
3443 win32_close_handle(*t
);
3453 void os_proc_free_handle(struct proc_handle
*ph
)
3455 ajla_assert_lo(list_is_empty(&ph
->wait_list
), (file_line
, "os_proc_free_handle: freeing handle when there are processes waiting for it"));
3458 monitor_close(ph
->process_handle
);
3463 bool os_proc_register_wait(struct proc_handle
*ph
, mutex_t
**mutex_to_lock
, struct list
*list_entry
, int *status
)
3467 *status
= ph
->exit_code
;
3471 *mutex_to_lock
= &monitor_mutex
;
3472 list_add(&ph
->wait_list
, list_entry
);
3479 static mutex_t signal_mutex
;
3486 struct list wait_list
;
3489 static BOOL WINAPI
signal_handler(DWORD sig
)
3493 mutex_lock(&signal_mutex
);
3494 if (!signals
[sig
].refcount
) {
3495 mutex_unlock(&signal_mutex
);
3499 call(wake_up_wait_list
)(&signals
[sig
].wait_list
, &signal_mutex
, false);
3503 int os_signal_handle(const char *str
, signal_seq_t
*seq
, ajla_error_t attr_unused
*err
)
3506 if (!strcmp(str
, "SIGINT")) {
3508 } else if (!strcmp(str
, "SIGBREAK")) {
3509 sig
= CTRL_BREAK_EVENT
;
3514 mutex_lock(&signal_mutex
);
3515 *seq
= signals
[sig
].seq
;
3516 signals
[sig
].refcount
++;
3517 mutex_unlock(&signal_mutex
);
3521 void os_signal_unhandle(int sig
)
3523 ajla_assert_lo(sig
>= 0 && sig
<= N_SIG
, (file_line
, "os_signal_unhandle: invalid signal %d", sig
));
3526 mutex_lock(&signal_mutex
);
3527 ajla_assert_lo(signals
[sig
].refcount
> 0, (file_line
, "os_signal_unhandle: refcount underflow"));
3528 signals
[sig
].refcount
--;
3529 mutex_unlock(&signal_mutex
);
3532 signal_seq_t
os_signal_seq(int sig
)
3535 ajla_assert_lo(sig
>= 0 && sig
<= N_SIG
, (file_line
, "os_signal_seq: invalid signal %d", sig
));
3538 mutex_lock(&signal_mutex
);
3539 seq
= signals
[sig
].seq
;
3540 mutex_unlock(&signal_mutex
);
3544 bool os_signal_wait(int sig
, signal_seq_t seq
, mutex_t
**mutex_to_lock
, struct list
*list_entry
)
3546 ajla_assert_lo(sig
>= 0 && sig
<= N_SIG
, (file_line
, "os_signal_wait: invalid signal %d", sig
));
3548 iomux_never(mutex_to_lock
, list_entry
);
3551 mutex_lock(&signal_mutex
);
3552 if (seq
!= signals
[sig
].seq
) {
3553 mutex_unlock(&signal_mutex
);
3556 *mutex_to_lock
= &signal_mutex
;
3557 list_add(&signals
[sig
].wait_list
, list_entry
);
3558 mutex_unlock(&signal_mutex
);
3563 struct win32_notify_handle
{
3565 struct list wait_list
;
3569 static void iomux_directory_handle_wake_up(void *nh_
)
3571 struct win32_notify_handle
*nh
= nh_
;
3573 win32_close_change_notification_handle(nh
->h
);
3574 call(wake_up_wait_list
)(&nh
->wait_list
, &monitor_mutex
, true);
3577 bool iomux_directory_handle_alloc(dir_handle_t handle
, notify_handle_t
*h
, uint64_t attr_unused
*seq
, ajla_error_t
*err
)
3579 struct win32_notify_handle
*nh
;
3581 nh
= mem_alloc_mayfail(struct win32_notify_handle
*, sizeof(struct win32_notify_handle
), err
);
3584 list_init(&nh
->wait_list
);
3587 WCHAR
*w
= utf8_to_wchar(handle
, err
);
3592 nh
->h
= FindFirstChangeNotificationW(w
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_SECURITY
);
3593 gle
= GetLastError();
3596 nh
->h
= FindFirstChangeNotificationA(handle
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_SECURITY
);
3597 gle
= GetLastError();
3599 if (unlikely(nh
->h
== INVALID_HANDLE_VALUE
)) {
3600 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
3601 fatal_mayfail(e
, err
, "can't wait on change of '%s': %s", handle
, error_decode(e
));
3605 if (unlikely(!monitor_handle(nh
->h
, iomux_directory_handle_wake_up
, win32_close_change_notification_handle
, nh
, err
))) {
3606 win32_close_change_notification_handle(nh
->h
);
3614 bool iomux_directory_handle_wait(notify_handle_t h
, uint64_t attr_unused seq
, mutex_t
**mutex_to_lock
, struct list
*list_entry
)
3616 struct win32_notify_handle
*nh
= h
;
3622 *mutex_to_lock
= &monitor_mutex
;
3623 list_add(&nh
->wait_list
, list_entry
);
3629 void iomux_directory_handle_free(notify_handle_t h
)
3631 struct win32_notify_handle
*nh
= h
;
3632 ajla_assert_lo(list_is_empty(&nh
->wait_list
), (file_line
, "iomux_directory_handle_free: freeing handle when there are processes waiting for it"));
3635 monitor_close(nh
->h
);
3641 static SOCKET win32_notify_socket
[2];
3643 static bool win32_socketpair(SOCKET result
[2])
3646 struct sockaddr_in sin
;
3651 lst
= INVALID_SOCKET
;
3652 result
[0] = result
[1] = INVALID_SOCKET
;
3654 lst
= socket(PF_INET
, SOCK_STREAM
, 0);
3655 if (unlikely(lst
== INVALID_SOCKET
))
3658 memset(&sin
, 0, sizeof sin
);
3659 sin
.sin_family
= AF_INET
;
3660 sin
.sin_addr
.s_addr
= htonl(0x7f000001);
3661 r
= bind(lst
, (struct sockaddr
*)&sin
, sizeof sin
);
3662 if (unlikely(r
== SOCKET_ERROR
))
3666 r
= getsockname(lst
, (struct sockaddr
*)&sin
, &len
);
3667 if (unlikely(r
== SOCKET_ERROR
))
3671 if (unlikely(r
== SOCKET_ERROR
))
3674 result
[0] = socket(PF_INET
, SOCK_STREAM
, 0);
3675 if (unlikely(result
[0] == INVALID_SOCKET
))
3678 r
= connect(result
[0], (struct sockaddr
*)&sin
, sizeof sin
);
3679 if (unlikely(r
== SOCKET_ERROR
))
3683 result
[1] = accept(lst
, (struct sockaddr
*)&sin
, &len
);
3684 if (unlikely(result
[1] == INVALID_SOCKET
))
3688 r
= ioctlsocket(result
[0], FIONBIO
, &one
);
3689 if (unlikely(r
== SOCKET_ERROR
))
3691 r
= ioctlsocket(result
[1], FIONBIO
, &one
);
3692 if (unlikely(r
== SOCKET_ERROR
))
3695 win32_close_socket(lst
);
3700 if (lst
!= INVALID_SOCKET
)
3701 win32_close_socket(lst
);
3702 if (result
[0] != INVALID_SOCKET
)
3703 win32_close_socket(result
[0]);
3704 if (result
[1] != INVALID_SOCKET
)
3705 win32_close_socket(result
[1]);
3709 static void win32_notify(void)
3713 r
= send(win32_notify_socket
[1], &c
, 1, 0);
3714 if (unlikely(r
== SOCKET_ERROR
)) {
3715 int er
= WSAGetLastError();
3716 if (unlikely(er
!= WSAEWOULDBLOCK
))
3717 fatal("error writing to the notify socket: %d", er
);
3721 static void win32_shutdown_notify_pipe(void)
3726 r
= send(win32_notify_socket
[1], &c
, 1, 0);
3727 if (unlikely(r
== SOCKET_ERROR
)) {
3728 int er
= WSAGetLastError();
3729 if (er
== WSAEWOULDBLOCK
) {
3733 fatal("error writing to the notify socket: %d", er
);
3737 static bool win32_drain_notify_pipe(void)
3741 r
= recv(win32_notify_socket
[0], buffer
, sizeof buffer
, 0);
3742 if (unlikely(r
== SOCKET_ERROR
)) {
3743 int er
= WSAGetLastError();
3744 if (likely(er
== WSAEWOULDBLOCK
))
3746 fatal("error reading the notify socket: %d", er
);
3748 return !!memchr(buffer
, 1, r
);
3752 handle_t
os_socket(int domain
, int type
, int protocol
, ajla_error_t
*err
)
3755 if (unlikely(!winsock_supported
)) {
3756 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not configured");
3759 domain
= os_socket_pf(domain
, err
);
3760 if (unlikely(domain
== -1))
3762 type
= os_socket_type(type
, err
);
3763 if (unlikely(type
== -1))
3765 sock
= socket(domain
, type
, protocol
);
3766 if (unlikely(sock
== INVALID_SOCKET
)) {
3767 fatal_mayfail(error_from_win32_socket(WSAGetLastError()), err
, "socket failed");
3770 return win32_socket_to_handle(sock
, err
);
3773 bool os_bind_connect(bool bnd
, handle_t h
, unsigned char *addr
, size_t addr_len
, ajla_error_t
*err
)
3777 struct sockaddr
*sa
;
3779 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3780 if (unlikely(!handle_is_socket(h
))) {
3781 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3784 sa
= os_get_sock_addr(addr
, &addr_len
, err
);
3787 r
= (likely(!bnd
) ? connect
: bind
)(h
->s
, sa
, addr_len
);
3788 er
= WSAGetLastError();
3789 mem_free_aligned(sa
);
3790 if (unlikely(r
!= SOCKET_ERROR
))
3792 if (likely(!bnd
) && (likely(er
== WSAEWOULDBLOCK
) || likely(!er
))) {
3793 /*debug("connect would block");
3795 int t1 = iomux_test_handle(h, false);
3796 int t2 = iomux_test_handle(h, true);
3798 debug("test handle: %d", t1, t2);
3803 socklen_t er_l = sizeof(er);
3804 r = getsockopt(h->s, SOL_SOCKET, SO_ERROR, cast_ptr(char *, &er), &er_l);
3805 if (unlikely(r == SOCKET_ERROR)) {
3806 er = WSAGetLastError();
3807 fatal_mayfail(error_from_win32_socket(er), err, "getsockopt returned an error: %d", er);
3810 debug("socket status: %d", er);
3813 h
->connect_in_progress
= true;
3816 fatal_mayfail(error_from_win32_socket(er
), err
, "can't %s socket: %d", !bnd
? "connect" : "bind", er
);
3820 bool os_connect_completed(handle_t h
, ajla_error_t
*err
)
3824 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3825 if (unlikely(!handle_is_socket(h
))) {
3826 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3830 e
= get_socket_error(h
);
3832 fatal_mayfail(error_from_win32_socket(e
), err
, "can't connect socket: %d", e
);
3835 h
->connect_in_progress
= false;
3839 bool os_listen(handle_t h
, ajla_error_t
*err
)
3844 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3845 if (unlikely(!handle_is_socket(h
))) {
3846 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3850 r
= listen(h
->s
, signed_maximum(int));
3851 if (unlikely(r
== SOCKET_ERROR
)) {
3852 er
= WSAGetLastError();
3853 fatal_mayfail(error_from_win32_socket(er
), err
, "listen returned an error: %d", er
);
3859 int os_accept(handle_t h
, handle_t
*result
, ajla_error_t
*err
)
3863 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3864 if (unlikely(!handle_is_socket(h
))) {
3865 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3869 sock
= accept(h
->s
, NULL
, 0);
3870 if (unlikely(sock
== INVALID_SOCKET
)) {
3871 int er
= WSAGetLastError();
3872 if (likely(er
== WSAEWOULDBLOCK
))
3873 return OS_RW_WOULDBLOCK
;
3874 fatal_mayfail(error_from_win32_socket(er
), err
, "accept returned an error: %d", er
);
3878 *result
= win32_socket_to_handle(sock
, err
);
3880 return unlikely(*result
== NULL
) ? OS_RW_ERROR
: 0;
3883 bool os_getsockpeername(bool peer
, handle_t h
, unsigned char **addr
, size_t *addr_len
, ajla_error_t
*err
)
3886 struct sockaddr
*sa
;
3889 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3890 if (unlikely(!handle_is_socket(h
))) {
3891 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3895 sa
= mem_align_mayfail(struct sockaddr
*, SOCKADDR_MAX_LEN
, SOCKADDR_ALIGN
, err
);
3898 addrlen
= SOCKADDR_MAX_LEN
;
3899 r
= (!peer
? getsockname
: getpeername
)(h
->s
, sa
, &addrlen
);
3900 if (r
== SOCKET_ERROR
) {
3901 int er
= WSAGetLastError();
3902 fatal_mayfail(error_from_win32_socket(er
), err
, "%s returned an error: %d", !peer
? "getsockname" : "getpeername", er
);
3903 goto free_ret_false
;
3906 *addr
= os_get_ajla_addr(sa
, &addrlen
, err
);
3907 if (unlikely(!*addr
))
3908 goto free_ret_false
;
3910 *addr_len
= addrlen
;
3912 mem_free_aligned(sa
);
3916 mem_free_aligned(sa
);
3920 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
)
3923 struct sockaddr
*sa
;
3926 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3927 if (unlikely(!handle_is_socket(h
))) {
3928 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3932 f
= translate_flags(os_socket_msg
, flags
, err
);
3933 if (unlikely(f
< 0))
3936 sa
= mem_align_mayfail(struct sockaddr
*, SOCKADDR_MAX_LEN
, SOCKADDR_ALIGN
, err
);
3939 addrlen
= SOCKADDR_MAX_LEN
;
3940 r
= recvfrom(h
->s
, buffer
, len
, f
, sa
, &addrlen
);
3941 if (unlikely(r
== SOCKET_ERROR
)) {
3942 int er
= WSAGetLastError();
3943 if (likely(er
== WSAEWOULDBLOCK
))
3944 return OS_RW_WOULDBLOCK
;
3945 fatal_mayfail(error_from_win32_socket(er
), err
, "recvfrom returned an error: %d", er
);
3946 goto free_ret_error
;
3948 if (unlikely(addrlen
> SOCKADDR_MAX_LEN
)) {
3949 fatal_mayfail(error_ajla(EC_SYSCALL
, AJLA_ERROR_SIZE_OVERFLOW
), err
, "the system returned too long address");
3950 goto free_ret_error
;
3954 if (unlikely(!array_init_mayfail(unsigned char, addr
, addr_len
, err
))) {
3955 goto free_ret_error
;
3958 *addr
= os_get_ajla_addr(sa
, &addrlen
, err
);
3959 if (unlikely(!*addr
))
3960 goto free_ret_error
;
3961 *addr_len
= addrlen
;
3964 mem_free_aligned(sa
);
3968 mem_free_aligned(sa
);
3972 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
)
3975 struct sockaddr
*sa
;
3978 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
3979 if (unlikely(!handle_is_socket(h
))) {
3980 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
3984 f
= translate_flags(os_socket_msg
, flags
, err
);
3985 if (unlikely(f
< 0))
3988 if (addr_len
!= 0) {
3989 size_t al
= addr_len
;
3990 sa
= os_get_sock_addr(addr
, &al
, err
);
3993 r
= sendto(h
->s
, buffer
, len
, f
, sa
, al
);
3994 er
= WSAGetLastError();
3995 mem_free_aligned(sa
);
3997 r
= send(h
->s
, buffer
, len
, f
);
3998 er
= WSAGetLastError();
4001 if (unlikely(r
== SOCKET_ERROR
)) {
4002 if (likely(er
== WSAEWOULDBLOCK
))
4003 return OS_RW_WOULDBLOCK
;
4004 fatal_mayfail(error_from_win32_socket(er
), err
, "send%s returned an error: %d", addr_len
? "to" : "", er
);
4011 bool os_getsockopt(handle_t h
, int level
, int option
, char **buffer
, size_t *buffer_len
, ajla_error_t
*err
)
4016 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
4017 if (unlikely(!handle_is_socket(h
))) {
4018 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
4022 level
= os_socket_level(level
, err
);
4023 if (unlikely(level
< 0))
4026 option
= os_socket_option(option
, err
);
4027 if (unlikely(level
< 0))
4032 *buffer
= mem_alloc_mayfail(char *, opt_len
, err
);
4033 if (unlikely(!*buffer
))
4036 r
= getsockopt(h
->s
, level
, option
, *buffer
, &opt_len
);
4038 if (unlikely(r
== SOCKET_ERROR
)) {
4039 int er
= WSAGetLastError();
4040 fatal_mayfail(error_from_win32_socket(er
), err
, "getsockopt returned an error: %d", er
);
4045 *buffer_len
= opt_len
;
4049 bool os_setsockopt(handle_t h
, int level
, int option
, const char *buffer
, size_t buffer_len
, ajla_error_t
*err
)
4053 obj_registry_verify(OBJ_TYPE_HANDLE
, ptr_to_num(h
), file_line
);
4054 if (unlikely(!handle_is_socket(h
))) {
4055 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "socket operation on non-socket");
4059 level
= os_socket_level(level
, err
);
4060 if (unlikely(level
< 0))
4063 option
= os_socket_option(option
, err
);
4064 if (unlikely(level
< 0))
4067 r
= setsockopt(h
->s
, level
, option
, buffer
, buffer_len
);
4069 if (unlikely(r
== SOCKET_ERROR
)) {
4070 int er
= WSAGetLastError();
4071 fatal_mayfail(error_from_win32_socket(er
), err
, "setsockopt returned an error: %d", er
);
4078 static bool os_use_getaddrinfo(const char *host
, int port
, struct address
**result
, size_t *result_l
, ajla_error_t
*err
)
4083 struct addrinfo
*res
= NULL
, *rs
;
4085 if (unlikely(!array_init_mayfail(struct address
, result
, result_l
, err
)))
4088 sprintf(port_str
, "%d", port
);
4089 r
= fn_getaddrinfo(host
, port_str
, NULL
, &res
);
4091 fatal_mayfail(error_ajla_aux(EC_SYSCALL
, AJLA_ERROR_GAI
, abs((int)r
)), err
, "host not found");
4095 for (rs
= res
; rs
; rs
= rs
->ai_next
) {
4097 struct address addr
;
4099 socklen_t addrlen
= rs
->ai_addrlen
;
4101 addr
.address
= os_get_ajla_addr(rs
->ai_addr
, &addrlen
, &e
);
4102 if (unlikely(!addr
.address
))
4104 addr
.address_length
= addrlen
;
4106 if (unlikely(!array_add_mayfail(struct address
, result
, result_l
, addr
, &xresult
, err
))) {
4112 if (unlikely(!*result_l
)) {
4113 fatal_mayfail(error_ajla_aux(EC_SYSCALL
, AJLA_ERROR_GAI
, abs(EAI_NONAME
)), err
, "host not found");
4117 fn_freeaddrinfo(res
);
4122 fn_freeaddrinfo(res
);
4123 for (i
= 0; i
< *result_l
; i
++)
4124 mem_free((*result
)[i
].address
);
4129 bool os_getaddrinfo(const char *host
, int port
, struct address
**result
, size_t *result_l
, ajla_error_t
*err
)
4136 if (unlikely(!winsock_supported
)) {
4137 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not configured");
4141 if (likely(fn_getaddrinfo
!= NULL
)) {
4142 return os_use_getaddrinfo(host
, port
, result
, result_l
, err
);
4145 if (unlikely(!array_init_mayfail(struct address
, result
, result_l
, err
)))
4148 he
= gethostbyname(host
);
4150 if (unlikely(!he
)) {
4151 int er
= WSAGetLastError();
4152 fatal_mayfail(error_from_win32_socket(er
), err
, "host not found");
4156 if (he
->h_addrtype
!= AF_INET
|| he
->h_length
!= 4 || !he
->h_addr
) {
4157 int er
= WSANO_DATA
;
4158 fatal_mayfail(error_from_win32_socket(er
), err
, "host not found");
4162 for (i
= 0; (a
= he
->h_addr_list
[i
]); i
++) {
4163 struct sockaddr_in sin
;
4165 struct address addr
;
4167 socklen_t addrlen
= sizeof sin
;
4169 sin
.sin_family
= AF_INET
;
4170 sin
.sin_port
= (port
<< 8) | (port
>> 8);
4171 memcpy(&sin
.sin_addr
, a
, 4);
4173 memcpy(&sa
, &sin
, sizeof sin
);
4175 addr
.address
= os_get_ajla_addr(&sa
, &addrlen
, &e
);
4176 if (unlikely(!addr
.address
))
4178 addr
.address_length
= addrlen
;
4180 if (unlikely(!array_add_mayfail(struct address
, result
, result_l
, addr
, &xresult
, err
))) {
4186 if (unlikely(!*result_l
)) {
4187 int er
= WSANO_DATA
;
4188 fatal_mayfail(error_from_win32_socket(er
), err
, "host not found");
4195 for (i
= 0; i
< *result_l
; i
++)
4196 mem_free((*result
)[i
].address
);
4201 static char *os_use_getnameinfo(unsigned char *addr
, size_t addr_len
, ajla_error_t
*err
)
4203 struct sockaddr
*sa
;
4207 sa
= os_get_sock_addr(addr
, &addr_len
, err
);
4212 h
= mem_alloc_mayfail(char *, h_len
, err
);
4214 mem_free_aligned(sa
);
4217 r
= fn_getnameinfo(sa
, addr_len
, h
, h_len
, NULL
, 0, 0);
4219 if (unlikely(r
== 122)) {
4222 if (unlikely(!h_len
)) {
4223 fatal_mayfail(error_ajla(EC_SYSCALL
, AJLA_ERROR_SIZE_OVERFLOW
), err
, "name buffer overflow");
4224 mem_free_aligned(sa
);
4227 goto alloc_buffer_again
;
4229 fatal_mayfail(error_ajla_aux(EC_SYSCALL
, AJLA_ERROR_GAI
, abs((int)r
)), err
, "host not found");
4231 mem_free_aligned(sa
);
4234 mem_free_aligned(sa
);
4238 char *os_getnameinfo(unsigned char *addr
, size_t addr_len
, ajla_error_t
*err
)
4240 struct sockaddr
*sa
;
4245 if (unlikely(!winsock_supported
)) {
4246 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "TCP/IP is not configured");
4250 if (likely(fn_getnameinfo
!= NULL
)) {
4251 return os_use_getnameinfo(addr
, addr_len
, err
);
4254 sa
= os_get_sock_addr(addr
, &addr_len
, err
);
4257 switch (sa
->sa_family
) {
4259 struct sockaddr_in
*sin
;
4260 if (unlikely(addr_len
< offsetof(struct sockaddr_in
, sin_addr
) + 4)) {
4261 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "too short address");
4262 mem_free_aligned(sa
);
4265 sin
= cast_ptr(struct sockaddr_in
*, sa
);
4266 he
= gethostbyaddr(cast_ptr(char *, &sin
->sin_addr
.s_addr
), 4, sa
->sa_family
);
4271 struct sockaddr_in6
*sin6
;
4272 if (unlikely(addr_len
< offsetof(struct sockaddr_in6
, sin6_addr
) + 16)) {
4273 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "too short address");
4274 mem_free_aligned(sa
);
4277 sin6
= cast_ptr(struct sockaddr_in6
*, sa
);
4278 he
= gethostbyaddr(&sin6
->sin6_addr
.s6_addr
, 16, sa
->sa_family
);
4283 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "address family %d not supported", sa
->sa_family
);
4284 mem_free_aligned(sa
);
4288 mem_free_aligned(sa
);
4289 if (unlikely(!he
) || !he
->h_name
) {
4290 fatal_mayfail(error_ajla_aux(EC_SYSCALL
, AJLA_ERROR_H_ERRNO
, h_errno
), err
, "host not found");
4293 le
= strlen(he
->h_name
);
4294 name
= mem_alloc_mayfail(char *, le
+ 1, err
);
4295 if (unlikely(!name
))
4297 return memcpy(name
, he
->h_name
, le
+ 1);
4300 bool os_getaddrinfo_is_thread_safe(void)
4302 return winsock_supported
&& fn_getaddrinfo
!= NULL
&& fn_getnameinfo
!= NULL
;
4305 const char *os_decode_error(ajla_error_t attr_unused error
, char attr_unused
*(*tls_buffer
)(void))
4311 struct dl_handle_t
*os_dlopen(const char *filename
, ajla_error_t
*err
, char **err_msg
)
4318 WCHAR
*w
= utf8_to_wchar(filename
, err
);
4321 for (i
= 0; w
[i
]; i
++)
4324 h
= LoadLibraryW(w
);
4325 gle
= GetLastError();
4328 char *a
= str_dup(filename
, -1, err
);
4331 for (i
= 0; a
[i
]; i
++)
4334 h
= LoadLibraryA(a
);
4335 gle
= GetLastError();
4339 ajla_error_t e
= error_from_win32(EC_SYSCALL
, gle
);
4340 fatal_mayfail(e
, err
, "can't load library %s: %s", filename
, error_decode(e
));
4343 return cast_ptr(struct dl_handle_t
*, h
);
4346 void os_dlclose(struct dl_handle_t
*dlh
)
4348 if (unlikely(!FreeLibrary(cast_ptr(HMODULE
, dlh
))))
4349 internal(file_line
, "FreeLibrary failed: %u", GetLastError());
4352 bool os_dlsym(struct dl_handle_t
*dlh
, const char *symbol
, void **result
)
4354 void *r
= GetProcAddress(cast_ptr(HMODULE
, dlh
), symbol
);
4362 static thread_t iomux_thread
;
4364 static int compare_socket(const void *x1
, const void *x2
)
4366 SOCKET s1
= *cast_ptr(const SOCKET
*, x1
);
4367 SOCKET s2
= *cast_ptr(const SOCKET
*, x2
);
4375 thread_function_decl(iomux_poll_thread
,
4377 struct fdx_set
*fdx
[2];
4382 fdx
[0] = mem_alloc(struct fdx_set
*, sizeof(struct fdx_set
));
4383 fdx
[1] = mem_alloc(struct fdx_set
*, sizeof(struct fdx_set
));
4385 while (likely(!win32_drain_notify_pipe())) {
4388 bool need_timeout
= false;
4389 struct timeval timeout
;
4390 timeout
.tv_sec
= CONNECT_TIMEOUT
/ 1000000;
4391 timeout
.tv_usec
= CONNECT_TIMEOUT
% 1000000;
4393 fdx
[0]->fd_count
= 1;
4394 fdx
[0]->fd_array
[0] = win32_notify_socket
[0];
4395 fdx
[1]->fd_count
= 0;
4397 mutex_lock(&socket_list_mutex
);
4398 for (wr
= 0; wr
< 2; wr
++) {
4400 list_for_each(l
, &socket_list
[wr
]) {
4401 handle_t h
= get_struct(l
, struct win32_handle
, socket_entry
[wr
]);
4402 address_lock(h
, DEPTH_THUNK
);
4403 if (!list_is_empty(&h
->wait_list
[wr
])) {
4404 if (unlikely(fdx
[wr
]->fd_count
== fdx_size
)) {
4406 if (unlikely(!fdx_size
))
4407 fatal("too many sockets");
4408 mem_check_overflow(offsetof(struct fdx_set
, fd_array
), fdx_size
, sizeof(SOCKET
), NULL
);
4409 fdx
[0] = mem_realloc(struct fdx_set
*, fdx
[0], offsetof(struct fdx_set
, fd_array
[fdx_size
]));
4410 fdx
[1] = mem_realloc(struct fdx_set
*, fdx
[1], offsetof(struct fdx_set
, fd_array
[fdx_size
]));
4412 fdx
[wr
]->fd_array
[fdx
[wr
]->fd_count
++] = h
->s
;
4413 if (unlikely(h
->connect_in_progress
))
4414 need_timeout
= true;
4417 list_del(&h
->socket_entry
[wr
]);
4418 h
->socket_entry
[wr
].next
= NULL
;
4420 address_unlock(h
, DEPTH_THUNK
);
4423 mutex_unlock(&socket_list_mutex
);
4425 r
= select(FD_SETSIZE
, cast_ptr(fd_set
*, fdx
[0]), cast_ptr(fd_set
*, fdx
[1]), NULL
, need_timeout
? &timeout
: NULL
);
4426 if (unlikely(r
== SOCKET_ERROR
)) {
4427 int er
= WSAGetLastError();
4428 internal(file_line
, "select returned an error: %d", er
);
4431 qsort(fdx
[0]->fd_array
, fdx
[0]->fd_count
, sizeof(SOCKET
), compare_socket
);
4432 qsort(fdx
[1]->fd_array
, fdx
[1]->fd_count
, sizeof(SOCKET
), compare_socket
);
4434 mutex_lock(&socket_list_mutex
);
4435 for (wr
= 0; wr
< 2; wr
++) {
4437 list_for_each(l
, &socket_list
[wr
]) {
4438 handle_t h
= get_struct(l
, struct win32_handle
, socket_entry
[wr
]);
4441 p
= bsearch(&hndl
, fdx
[wr
]->fd_array
, fdx
[wr
]->fd_count
, sizeof(SOCKET
), compare_socket
);
4444 address_lock(h
, DEPTH_THUNK
);
4445 call(wake_up_wait_list
)(&h
->wait_list
[wr
], address_get_mutex(h
, DEPTH_THUNK
), true);
4447 if (unlikely(h
->connect_in_progress
)) {
4448 int e
= get_socket_error(h
);
4455 mutex_unlock(&socket_list_mutex
);
4463 int os_getpagesize(void)
4470 ps
= si
.dwAllocationGranularity
;
4474 static DWORD
prot_to_protect(int prot
)
4476 if (prot
== PROT_NONE
)
4477 return PAGE_NOACCESS
;
4478 if (prot
== PROT_READ
)
4479 return PAGE_READONLY
;
4480 if (prot
== (PROT_READ
| PROT_WRITE
))
4481 return PAGE_READWRITE
;
4482 return PAGE_EXECUTE_READWRITE
;
4485 void *os_mmap(void *ptr
, size_t size
, int prot
, int flags
, handle_t h
, os_off_t off
, ajla_error_t
*err
)
4488 DWORD allocation_type
, protect
;
4492 align
= (size_t)1 << MAP_ALIGNED_BITS(flags
);
4493 allocation_type
= MEM_RESERVE
| (prot
== PROT_NONE
? 0 : MEM_COMMIT
);
4494 protect
= prot_to_protect(prot
);
4496 if (unlikely((flags
& (MAP_EXCL
| MAP_FIXED
)) == MAP_FIXED
)) {
4497 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "fixed mappings not supported");
4500 if (unlikely(handle_is_valid(h
))) {
4502 DWORD map_access
= !prot
|| prot
== PROT_READ
? FILE_MAP_READ
: FILE_MAP_WRITE
;
4503 if (unlikely(handle_is_socket(h
))) {
4504 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_NOT_SUPPORTED
), err
, "mmap of socket not supported");
4507 hmap
= CreateFileMappingA(h
->h
, NULL
, protect
, 0, 0, NULL
);
4508 if (unlikely(!hmap
)) {
4509 e
= error_from_win32(EC_SYSCALL
, GetLastError());
4510 fatal_mayfail(e
, err
, "can't create mapping object: %s", error_decode(e
));
4514 res
= MapViewOfFileEx(hmap
, map_access
, off
>> 31 >> 1, off
, size
, ptr
);
4515 if (unlikely(!res
)) {
4516 if (ptr
&& !(flags
& MAP_FIXED
)) {
4518 goto repeat_filemap
;
4520 e
= error_from_win32(EC_SYSCALL
, GetLastError());
4521 win32_close_handle(hmap
);
4522 fatal_mayfail(e
, err
, "can't map file: %s", error_decode(e
));
4525 if (ptr
&& unlikely(res
!= ptr
))
4526 internal(file_line
, "MapViewOfFileEx allocated different memory: %p != %p", res
, ptr
);
4527 win32_close_handle(hmap
);
4531 res
= VirtualAlloc(ptr
, size
+ align
- 1, allocation_type
, protect
);
4532 if (unlikely(!res
)) {
4533 if (ptr
&& !(flags
& MAP_FIXED
)) {
4537 e
= error_from_win32(EC_SYSCALL
, GetLastError());
4538 fatal_mayfail(e
, err
, "can't allocate memory: %s", error_decode(e
));
4541 if (ptr
&& unlikely(res
!= ptr
))
4542 internal(file_line
, "VirtualAlloc allocated different memory: %p != %p", res
, ptr
);
4544 void *aptr
= num_to_ptr(round_up(ptr_to_num(res
), align
));
4545 if (unlikely(!VirtualFree(res
, 0, MEM_RELEASE
)))
4546 internal(file_line
, "VirtualFree failed: %u", GetLastError());
4547 res
= VirtualAlloc(aptr
, size
, allocation_type
, protect
);
4550 if (unlikely(res
!= aptr
)) {
4551 internal(file_line
, "VirtualAlloc allocated different memory: %p != %p", res
, aptr
);
4558 void os_munmap(void *ptr
, size_t attr_unused size
, bool file
)
4561 if (unlikely(!VirtualFree(ptr
, 0, MEM_RELEASE
)))
4562 internal(file_line
, "VirtualFree failed: %u", GetLastError());
4564 if (unlikely(!UnmapViewOfFile(ptr
)))
4565 internal(file_line
, "UnmapViewOfFile failed: %u", GetLastError());
4569 bool os_mprotect(void *ptr
, size_t size
, int prot
, ajla_error_t
*err
)
4571 if (prot
== PROT_NONE
) {
4572 if (unlikely(!VirtualFree(ptr
, size
, MEM_DECOMMIT
)))
4573 internal(file_line
, "VirtualFree failed: %u", GetLastError());
4575 DWORD allocation_type
, protect
;
4576 allocation_type
= MEM_COMMIT
;
4577 protect
= prot_to_protect(prot
);
4578 if (unlikely(!VirtualAlloc(ptr
, size
, allocation_type
, protect
))) {
4579 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4580 fatal_mayfail(e
, err
, "can't commit memory: %s", error_decode(e
));
4587 void os_code_invalidate_cache(uint8_t *code
, size_t code_size
, bool set_exec
)
4590 if (fn_FlushInstructionCache
) {
4591 if (unlikely(!fn_FlushInstructionCache(GetCurrentProcess(), cast_ptr(void *, code
), code_size
))) {
4592 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4593 fatal("failed to flush instruction cache: FlushInstructionCache(%p, %"PRIxMAX
") returned error: %s", code
, (uintmax_t)code_size
, error_decode(e
));
4597 if (unlikely(!VirtualProtect(code
, code_size
, PAGE_EXECUTE_READWRITE
, &old
))) {
4598 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4599 fatal("failed to set memory range read+write+exec: VirtualProtect(%p, %"PRIxMAX
") returned error: %s", code
, (uintmax_t)code_size
, error_decode(e
));
4604 void *os_code_map(uint8_t *code
, size_t attr_unused code_size
, ajla_error_t attr_unused
*err
)
4606 /*debug("making executable: %p, %lx", code, code_size);*/
4607 os_code_invalidate_cache(code
, code_size
, true);
4611 void os_code_unmap(void *mapped_code
, size_t attr_unused code_size
)
4613 mem_free(mapped_code
);
4617 void os_get_environment(char **str
, size_t *l
)
4619 array_init(char, str
, l
);
4620 if (!is_winnt() || !fn_GetEnvironmentStringsW
|| !fn_FreeEnvironmentStringsW
) {
4622 if (fn_GetEnvironmentStringsA
)
4623 e
= fn_GetEnvironmentStringsA();
4625 e
= fn_GetEnvironmentStrings();
4628 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4629 fatal("GetEnvironmentStringsA failed: %s", error_decode(e
));
4640 if (c
>= 'a' && c
<= 'z')
4645 array_add(char, str
, l
, c
);
4647 array_add(char, str
, l
, 0);
4650 if (fn_GetEnvironmentStringsA
&& fn_FreeEnvironmentStringsA
) {
4651 if (unlikely(!fn_FreeEnvironmentStringsA(e
))) {
4652 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4653 fatal("FreeEnvironmentStringsA failed: %s", error_decode(e
));
4658 e
= fn_GetEnvironmentStringsW();
4661 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4662 fatal("GetEnvironmentStringsW failed: %s", error_decode(e
));
4670 utf8_str
= wchar_to_utf8(NULL
, a
, &err
);
4671 if (unlikely(!utf8_str
)) {
4672 if (err
.error_class
== EC_ASYNC
) {
4673 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4674 fatal("can't allocate environment memory: %s", error_decode(e
));
4678 utf8_len
= strlen(utf8_str
);
4679 for (i
= 0; i
< utf8_len
; i
++) {
4680 if (utf8_str
[i
] == '=')
4682 if (utf8_str
[i
] >= 'a' && utf8_str
[i
] <= 'z')
4683 utf8_str
[i
] -= 0x20;
4685 array_add_multiple(char, str
, l
, utf8_str
, utf8_len
+ 1);
4692 if (unlikely(!fn_FreeEnvironmentStringsW(e
))) {
4693 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4694 fatal("FreeEnvironmentStringsW failed: %s", error_decode(e
));
4700 void iomux_init(void)
4704 void iomux_done(void)
4712 for (i
= 0; i
< n_array_elements(win32_error_to_system_error
) - 1; i
++)
4713 if (unlikely(win32_error_to_system_error
[i
].errn
>= win32_error_to_system_error
[i
+ 1].errn
))
4714 internal(file_line
, "os_init: win32_error_to_system_error is not monotonic at %u", i
);
4716 os_threads_initialized
= false;
4720 store_relaxed(&window_offset_x
, 0);
4721 store_relaxed(&window_offset_y
, 0);
4723 os_cwd
= os_dir_cwd(NULL
);
4724 os_init_path_to_exe();
4726 fn_GetEnvironmentStrings
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetEnvironmentStrings");
4727 fn_GetEnvironmentStringsA
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetEnvironmentStringsA");
4728 fn_GetEnvironmentStringsW
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetEnvironmentStringsW");
4729 fn_FreeEnvironmentStringsA
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeEnvironmentStringsA");
4730 fn_FreeEnvironmentStringsW
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeEnvironmentStringsW");
4731 if (!fn_GetEnvironmentStrings
)
4732 fatal("Could not get GetEnvironmentStrings");
4733 fn_RtlGetVersion
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
4734 fn_RtlFreeUserThreadStack
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlFreeUserThreadStack");
4735 fn_GetDiskFreeSpaceExA
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
4736 fn_GetDiskFreeSpaceExW
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExW");
4737 fn_CancelIo
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CancelIo");
4738 fn_CancelIoEx
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CancelIoEx");
4739 fn_MoveFileExA
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "MoveFileExA");
4740 fn_MoveFileExW
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "MoveFileExW");
4741 fn_FlushInstructionCache
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FlushInstructionCache");
4742 handle_iphlpa
= LoadLibraryA("iphlpapi.dll");
4744 fn_GetNetworkParams
= (void *)GetProcAddress(handle_iphlpa
, "GetNetworkParams");
4746 fn_GetTickCount64
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetTickCount64");
4749 fn_QueryPerformanceFrequency
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "QueryPerformanceFrequency");
4750 if (likely(fn_QueryPerformanceFrequency
!= NULL
)) {
4752 int64_t perf_frequency
;
4753 if (likely(fn_QueryPerformanceFrequency(&li
))) {
4754 perf_frequency
= li
.LowPart
+ ((uint64_t)li
.HighPart
<< 32);
4755 perf_multiplier
= (long double)1000000 / (long double)perf_frequency
;
4756 fn_QueryPerformanceCounter
= (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "QueryPerformanceCounter");
4761 fn_getaddrinfo
= (void *)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "getaddrinfo");
4762 fn_freeaddrinfo
= (void *)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "freeaddrinfo");
4763 if (unlikely(!fn_getaddrinfo
) || unlikely(!fn_freeaddrinfo
)) {
4764 fn_getaddrinfo
= NULL
;
4765 fn_freeaddrinfo
= NULL
;
4767 fn_getnameinfo
= (void *)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "getnameinfo");
4770 void os_init_multithreaded(void)
4776 os_init_calendar_lock();
4780 if (!fn_CancelIoEx
) {
4782 mutex_init(&pipe_count_mutex
);
4784 mutex_init(&tick_mutex
);
4785 list_init(&deferred_write_list
);
4786 list_init(&deferred_closed_list
);
4787 mutex_init(&deferred_mutex
);
4788 os_threads_initialized
= true;
4789 for (u
= 0; u
< 3; u
++)
4790 win32_std_handles
[u
] = win32_hfile_to_handle(get_std_handle(u
), (!u
? O_RDONLY
: O_WRONLY
) | O_NONBLOCK
, false, "", NULL
);
4796 debug("X1: %d", i
++);
4797 win32_create_read_thread(win32_std_handles
[0], NULL
);
4798 /*win32_terminate_io_thread(&win32_std_handles[0]->rd);*/
4799 os_free_handle(win32_std_handles
[0], false);
4800 win32_std_handles
[0] = win32_hfile_to_handle(get_std_handle(0), O_RDONLY
| O_NONBLOCK
, false, "", NULL
);
4811 debug("X2: %d", i
++);
4812 os_pipe(p
, 3, NULL
);
4813 r
= os_read(p
[0], &c
, 1, NULL
);
4821 wsaret
= WSAStartup(MAKEWORD(1, 1), &wsadata
);
4822 winsock_supported
= !wsaret
;
4823 if (winsock_supported
) {
4824 if (unlikely(!win32_socketpair(win32_notify_socket
))) {
4826 warning("WSACleanup returned an error: %u", WSAGetLastError());
4827 winsock_supported
= false;
4830 if (winsock_supported
) {
4831 mutex_init(&socket_list_mutex
);
4832 list_init(&socket_list
[0]);
4833 list_init(&socket_list
[1]);
4834 thread_spawn(&iomux_thread
, iomux_poll_thread
, NULL
, PRIORITY_IO
, NULL
);
4837 mutex_init(&signal_mutex
);
4838 memset(signals
, 0, sizeof signals
);
4839 for (u
= 0; u
< N_SIG
; u
++)
4840 list_init(&signals
[u
].wait_list
);
4842 if (unlikely(!SetConsoleCtrlHandler(signal_handler
, TRUE
))) {
4843 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4844 fatal("SetConsoleCtrlHandler set failed: %s", error_decode(e
));
4848 void os_done_multithreaded(void)
4852 if (unlikely(!SetConsoleCtrlHandler(signal_handler
, FALSE
))) {
4853 ajla_error_t e
= error_from_win32(EC_SYSCALL
, GetLastError());
4854 fatal("SetConsoleCtrlHandler unset failed: %s", error_decode(e
));
4857 mutex_done(&signal_mutex
);
4859 if (winsock_supported
) {
4860 win32_shutdown_notify_pipe();
4861 thread_join(&iomux_thread
);
4862 win32_close_socket(win32_notify_socket
[0]);
4863 win32_close_socket(win32_notify_socket
[1]);
4864 ajla_assert_lo(list_is_empty(&socket_list
[0]), (file_line
, "os_done_multithreaded: read socket list is not empty"));
4865 ajla_assert_lo(list_is_empty(&socket_list
[1]), (file_line
, "os_done_multithreaded: write socket list is not empty"));
4866 mutex_done(&socket_list_mutex
);
4868 warning("WSACleanup returned an error: %u", WSAGetLastError());
4869 winsock_supported
= false;
4872 for (u
= 0; u
< 3; u
++)
4873 os_free_handle(win32_std_handles
[u
], false);
4875 mutex_lock(&deferred_mutex
);
4876 while (!list_is_empty(&deferred_write_list
)) {
4877 mutex_unlock(&deferred_mutex
);
4879 mutex_lock(&deferred_mutex
);
4881 mutex_unlock(&deferred_mutex
);
4882 win32_clean_up_handles();
4884 mutex_done(&tick_mutex
);
4886 mutex_done(&pipe_count_mutex
);
4890 os_done_calendar_lock();
4892 os_threads_initialized
= false;
4897 mem_free(os_path_to_exe
);
4898 os_dir_close(os_cwd
);