/ scripts / generate-rpc-wrappers.py
generate-rpc-wrappers.py
   1  #!/usr/bin/env python3
   2  
   3  import os
   4  import sys
   5  from collections import OrderedDict
   6  import textwrap
   7  from datetime import datetime
   8  
   9  XNU_TRAP_CALL          = 1 << 0
  10  XNU_TRAP_NOPREFIX      = 1 << 1
  11  XNU_TRAP_NOSUFFIX      = 1 << 2
  12  XNU_TRAP_NOSUFFIX_ARGS = 1 << 3
  13  XNU_TRAP_BSD           = 1 << 4
  14  XNU_TRAP_NO_DTAPE_DEF  = 1 << 5
  15  XNU_BSD_TRAP_CALL      = XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX | XNU_TRAP_NOSUFFIX_ARGS | XNU_TRAP_BSD
  16  UNMANAGED_CALL         = 1 << 6
  17  ALLOW_INTERRUPTIONS    = 1 << 7
  18  PUSH_UNKNOWN_REPLIES   = 1 << 8
  19  
  20  # NOTE: in Python 3.7+, we can rely on dictionaries having their items in insertion order.
  21  #       unfortunately, we can't expect everyone building Darling to have Python 3.7+ installed.
  22  calls = [
  23  	#
  24  	# FORMAT:
  25  	# tuple with either 3 or 4 members:
  26  	#   1. call name: the name of the remote procedure
  27  	#   2. call parameters: the set of parameters callers are expected to provide arguments for.
  28  	#   3. return parameters: the set of parameters the procedure is expected to return values for.
  29  	#   4. flags: an optional set of flags that modify how this call is processed and wrapped.
  30  	# if the set of flags (4) is omitted, it defaults to 0.
  31  	#
  32  	# PARAMETERS:
  33  	# each parameter (both for calls and returns) is a tuple with either 2 or 3 members:
  34  	#   1. parameter name: the name of the parameter (duh)
  35  	#   2. public type: the type used in the public RPC wrappers
  36  	#   3. (optional) private type: the type used internally for serialization and for the server implementation.
  37  	# if the private type (3) is omitted, it is the same as the public type.
  38  	#
  39  	# TYPES:
  40  	# the types that can be used are normal C types. however, to be more architecture-agnostic,
  41  	# it is recommended to use `stdint.h` types whenever possible (e.g. `int32_t` instead of `int`,
  42  	# `uint64_t` instead of `unsigned long`, etc.).
  43  	#
  44  	# it is VERY IMPORTANT that pointer types ALWAYS have a distinct, fixed-size integral private type
  45  	# that is wide enough to accommodate pointers for all architectures. a good choice is `uint64_t`;
  46  	# NOT `uintptr_t`, as its size varies according to the architecture.
  47  	#
  48  	# SPECIAL TYPES:
  49  	# one special type that is supported is `@fd`. this type indicates that the parameter specifies a file descriptor.
  50  	# it will be treated as an `int` type-wise, but the RPC wrappers will perform some additional work on it
  51  	# to serialize it across the connection. this works bi-directionally (i.e. both the client and server can send and receive FDs).
  52  	# the resulting descriptor received on the other end (in either client or server) will behave like a `dup()`ed descriptor.
  53  	#
  54  	# FLAGS:
  55  	#   XNU_TRAP_CALL
  56  	#     this indicates that the given call is actually an XNU trap. this enables more advanced wrappers to be generated for that call
  57  	#     and avoid unnecessary boilerplate code on the server side.
  58  	#   XNU_TRAP_NOPREFIX
  59  	#     this indicates that the XNU trap does not use the `_kernelrpc_` prefix on the server side.
  60  	#     this affects both the function and argument structure names.
  61  	#   XNU_TRAP_NOSUFFIX
  62  	#     this indicates that the XNU trap does not use the `_trap` suffix on the server side.
  63  	#     this affects both the function and argument structure names.
  64  	#   XNU_TRAP_NOSUFFIX_ARGS
  65  	#     this indicates the arguments structure for this call on the server side does not use the `_trap` suffix.
  66  	#     this does not affect the name of the function on the server side.
  67  	#   XNU_TRAP_BSD
  68  	#     this indicates the XNU trap is for a BSD syscall. BSD syscalls have 2 return codes: one for failure and one for success.
  69  	#     this flag informs the RPC wrapper generator about this so it can handle it appropriately.
  70  	#   XNU_TRAP_NO_DTAPE_DEF
  71  	#     by default, the RPC wrapper generator code will generate duct-tape wrappers for XNU traps that automatically call the
  72  	#     XNU handler function for the trap. this flag tells it not to do that; this means you must define the duct-tape handler yourself.
  73  	#   UNMANAGED_CALL
  74  	#     this indicates that the given call may be called from an unmanaged process; that is, a process that the server does not manage or
  75  	#     have direct access to (e.g. it cannot access its memory). this is mainly useful for calls that inspect the state of the container.
  76  	#   ALLOW_INTERRUPTIONS
  77  	#     by default, calls are performed atomically with signals disabled on the calling thread; this way, the call is either fully performed
  78  	#     or fully unperformed. this flag indicates that the call should be allowed to be interrupted by signals. most calls should be performed
  79  	#     without interruptions, but calls that may wait (usually for long periods of time) should be performed with interruptions allowed.
  80  	#
  81  	#     do note that this means callers may see the value of `dserver_rpc_hooks_get_interrupt_status()` on return and must handle it appropriately.
  82  	#     this status is only returned when the RPC send operation is interrupted; when the RPC receive operation is interrupted, it is simply retried.
  83  	#     thus, even when interruptions are allowed, callers should still see a consistent RPC state.
  84  	#   PUSH_UNKNOWN_REPLIES
  85  	#     the vast majority of calls should fail (spectacularly) when they receive an unexpected/unknown reply from the server.
  86  	#     99% of the time, this is indicative of a critical RPC error. some calls (currently only one: interrupt_enter), however, need to gracefully handle
  87  	#     mixed up replies because of race conditions.
  88  	#
  89  	#     for example, when a signal is received, interrupt_enter is called. sometimes, signals arrive while we're waiting for a reply from the server
  90  	#     for another call. when the server receives interrupt_enter, it interrupts the current call and any reply it generates is deferred to be delivered
  91  	#     once the server receives interrupt_exit. however, there is still a race condition here: if the server already had the reply to the interrupted
  92  	#     call queued for delivery when the signal was received, the client will send interrupt_enter and immediately receive the reply to the interrupted
  93  	#     call. without handling this gracefully (by saving the reply for later), RPC communication becomes desynchronized and the program crashes.
  94  	#
  95  	#     note that this flag should only be used in very special circumstances (interrupt_enter currently being the only such one).
  96  	#     not only can it mask legitimate RPC communication errors, but it also requires significantly more stack space to handle such calls,
  97  	#     as the wrapper must create a buffer large enough to store any possible reply (including any potential descriptors).
  98  	#
  99  	#     the way this works is that calls with this flag allocate enough space in the reply buffer to hold all possible replies;
 100  	#     if they receive an unexpected reply, they push it back to the server. the server then holds on to the reply
 101  	#     and re-sends it when appropriate (e.g. for interrupt_enter, that's after interrupt_exit is called).
 102  	#
 103  	# TODO: we should probably add a class for these calls (so it's more readable).
 104  	#       we could even create a DSL (à-la-MIG), but that's probably overkill since
 105  	#       we only use our RPC for darlingserver calls.
 106  	#
 107  
 108  	('checkin', [
 109  		('is_fork', 'bool'),
 110  		('stack_hint', 'void*', 'uint64_t'),
 111  		('lifetime_listener_pipe', '@fd')
 112  	], []),
 113  
 114  	('checkout', [
 115  		('exec_listener_pipe', '@fd'),
 116  		('executing_macho', 'bool'),
 117  	], []),
 118  
 119  	('vchroot_path', [
 120  		('buffer', 'char*', 'uint64_t'),
 121  		('buffer_size', 'uint64_t'),
 122  	], [
 123  		('length', 'uint64_t'),
 124  	]),
 125  
 126  	('kprintf', [
 127  		('string', 'const char*', 'uint64_t'),
 128  		('string_length', 'uint64_t'),
 129  	], []),
 130  
 131  	('started_suspended', [], [
 132  		('suspended', 'bool'),
 133  	]),
 134  
 135  	('get_tracer', [], [
 136  		('tracer', 'int32_t'),
 137  	]),
 138  
 139  	('uidgid', [
 140  		('new_uid', 'int32_t'),
 141  		('new_gid', 'int32_t'),
 142  	], [
 143  		('old_uid', 'int32_t'),
 144  		('old_gid', 'int32_t'),
 145  	]),
 146  
 147  	('set_thread_handles', [
 148  		('pthread_handle', 'uint64_t'),
 149  		('dispatch_qaddr', 'uint64_t'),
 150  	], []),
 151  
 152  	('vchroot', [
 153  		('directory_fd', '@fd'),
 154  	], []),
 155  
 156  	('mldr_path', [
 157  		('buffer', 'char*', 'uint64_t'),
 158  		('buffer_size', 'uint64_t'),
 159  	], [
 160  		('length', 'uint64_t'),
 161  	]),
 162  
 163  	('fork_wait_for_child', [], [], ALLOW_INTERRUPTIONS),
 164  
 165  	('sigprocess', [
 166  		('bsd_signal_number', 'int32_t'),
 167  		('linux_signal_number', 'int32_t'),
 168  		('sender_pid', 'int32_t'),
 169  		('code', 'int32_t'),
 170  		('signal_address', 'uint64_t'),
 171  
 172  		# these are in/out pointers
 173  		('thread_state', 'uint64_t'),
 174  		('float_state', 'uint64_t'),
 175  	], [
 176  		('new_bsd_signal_number', 'int32_t'),
 177  	]),
 178  
 179  	('task_is_64_bit', [
 180  		('id', 'int32_t'),
 181  	], [
 182  		('is_64_bit', 'bool'),
 183  	]),
 184  
 185  	('interrupt_enter', [], [], PUSH_UNKNOWN_REPLIES),
 186  
 187  	('interrupt_exit', [], []),
 188  
 189  	('console_open', [], [
 190  		('console', '@fd'),
 191  	]),
 192  
 193  	('set_dyld_info', [
 194  		('address', 'uint64_t'),
 195  		('length', 'uint64_t'),
 196  	], []),
 197  
 198  	('stop_after_exec', [], []),
 199  
 200  	('set_tracer', [
 201  		('target', 'int32_t'),
 202  		('tracer', 'int32_t'),
 203  	], []),
 204  
 205  	('tid_for_thread', [
 206  		('thread', 'uint32_t'),
 207  	], [
 208  		('tid', 'int32_t'),
 209  	]),
 210  
 211  	('ptrace_sigexc', [
 212  		('target', 'int32_t'),
 213  		('enabled', 'bool'),
 214  	], []),
 215  
 216  	('ptrace_thupdate', [
 217  		('target', 'int32_t'),
 218  		('signum', 'int32_t'),
 219  	], []),
 220  
 221  	('thread_suspended', [
 222  		# these are in/out pointers
 223  		('thread_state', 'uint64_t'),
 224  		('float_state', 'uint64_t'),
 225  	], []),
 226  
 227  	('s2c_perform', [], []),
 228  
 229  	('set_executable_path', [
 230  		('buffer', 'const char*', 'uint64_t'),
 231  		('buffer_size', 'uint64_t')
 232  	], []),
 233  
 234  	('get_executable_path', [
 235  		('pid', 'int32_t'),
 236  		('buffer', 'char*', 'uint64_t'),
 237  		('buffer_size', 'uint64_t')
 238  	], [
 239  		('length', 'uint64_t'),
 240  	]),
 241  
 242  	('groups', [
 243  		('new_groups', 'const uint32_t*', 'uint64_t'),
 244  		('new_group_count', 'uint64_t'),
 245  		('old_groups', 'uint32_t*', 'uint64_t'),
 246  		('old_group_space', 'uint64_t'),
 247  	], [
 248  		('old_group_count', 'uint64_t'),
 249  	]),
 250  
 251  	#
 252  	# kqueue channels
 253  	#
 254  
 255  	('kqchan_mach_port_open', [
 256  		('port_name', 'uint32_t'),
 257  		('receive_buffer', 'void*', 'uint64_t'),
 258  		('receive_buffer_size', 'uint64_t'),
 259  		('saved_filter_flags', 'uint64_t'),
 260  	], [
 261  		('socket', '@fd'),
 262  	]),
 263  
 264  	('kqchan_proc_open', [
 265  		('pid', 'int32_t'),
 266  		('flags', 'uint32_t'),
 267  	], [
 268  		('socket', '@fd'),
 269  	]),
 270  
 271  	#
 272  	# pthread cancelation
 273  	#
 274  
 275  	('pthread_kill', [
 276  		('thread_port', 'uint32_t'),
 277  		('signal', 'int32_t'),
 278  	], []),
 279  
 280  	('pthread_canceled', [
 281  		('action', 'int32_t'),
 282  	], []),
 283  
 284  	('pthread_markcancel', [
 285  		('thread_port', 'uint32_t'),
 286  	], []),
 287  
 288  	#
 289  	# Mach IPC traps
 290  	#
 291  
 292  	('task_self_trap', [], [
 293  		('port_name', 'uint32_t'),
 294  	]),
 295  
 296  	('host_self_trap', [], [
 297  		('port_name', 'uint32_t'),
 298  	]),
 299  
 300  	('thread_self_trap', [], [
 301  		('port_name', 'uint32_t'),
 302  	]),
 303  
 304  	('mach_reply_port', [], [
 305  		('port_name', 'uint32_t'),
 306  	]),
 307  
 308  	('thread_get_special_reply_port', [], [
 309  		('port_name', 'uint32_t'),
 310  	]),
 311  
 312  	('mach_msg_overwrite', [
 313  		('msg', 'void*', 'uint64_t'),
 314  		('option', 'int32_t'),
 315  		('send_size', 'uint32_t'),
 316  		('rcv_size', 'uint32_t'),
 317  		('rcv_name', 'uint32_t'),
 318  		('timeout', 'uint32_t'),
 319  		('priority', 'uint32_t'),
 320  		('rcv_msg', 'void*', 'uint64_t'),
 321  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS),
 322  
 323  	('mach_port_deallocate', [
 324  		('target', 'uint32_t'),
 325  		('name', 'uint32_t'),
 326  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 327  
 328  	('mach_port_allocate', [
 329  		('target', 'uint32_t'),
 330  		('right', 'int32_t'),
 331  
 332  		# this would be better as a return parameter,
 333  		# but due to the way darlingserver handles Mach IPC calls,
 334  		# we need a pointer into the calling process's memory
 335  		('name', 'uint32_t*', 'uint64_t'),
 336  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 337  
 338  	('mach_port_mod_refs', [
 339  		('target', 'uint32_t'),
 340  		('name', 'uint32_t'),
 341  		('right', 'int32_t'),
 342  		('delta', 'int32_t'),
 343  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 344  
 345  	('mach_port_move_member', [
 346  		('target', 'uint32_t'),
 347  		('member', 'uint32_t'),
 348  		('after', 'uint32_t'),
 349  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 350  
 351  	('mach_port_insert_right', [
 352  		('target', 'uint32_t'),
 353  		('name', 'uint32_t'),
 354  		('poly', 'uint32_t'),
 355  		('polyPoly', 'int32_t'),
 356  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 357  
 358  	('mach_port_insert_member', [
 359  		('target', 'uint32_t'),
 360  		('name', 'uint32_t'),
 361  		('pset', 'uint32_t'),
 362  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 363  
 364  	('mach_port_extract_member', [
 365  		('target', 'uint32_t'),
 366  		('name', 'uint32_t'),
 367  		('pset', 'uint32_t'),
 368  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 369  
 370  	('mach_port_construct', [
 371  		('target', 'uint32_t'),
 372  		('options', 'void*', 'uint64_t'),
 373  		('context', 'uint64_t'),
 374  		('name', 'uint32_t*', 'uint64_t'),
 375  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 376  
 377  	('mach_port_destruct', [
 378  		('target', 'uint32_t'),
 379  		('name', 'uint32_t'),
 380  		('srdelta', 'int32_t'),
 381  		('guard', 'uint64_t'),
 382  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 383  
 384  	('mach_port_guard', [
 385  		('target', 'uint32_t'),
 386  		('name', 'uint32_t'),
 387  		('guard', 'uint64_t'),
 388  		('strict', 'bool'),
 389  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 390  
 391  	('mach_port_unguard', [
 392  		('target', 'uint32_t'),
 393  		('name', 'uint32_t'),
 394  		('guard', 'uint64_t'),
 395  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 396  
 397  	('mach_port_request_notification', [
 398  		('target', 'uint32_t'),
 399  		('name', 'uint32_t'),
 400  		('msgid', 'int32_t'),
 401  		('sync', 'uint32_t'),
 402  		('notify', 'uint32_t'),
 403  		('notifyPoly', 'uint32_t'),
 404  		('previous', 'uint32_t*', 'uint64_t'),
 405  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 406  
 407  	('mach_port_get_attributes', [
 408  		('target', 'uint32_t'),
 409  		('name', 'uint32_t'),
 410  		('flavor', 'int32_t'),
 411  		('info', 'void*', 'uint64_t'),
 412  		('count', 'uint32_t*', 'uint64_t'),
 413  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 414  
 415  	('mach_port_type', [
 416  		('target', 'uint32_t'),
 417  		('name', 'uint32_t'),
 418  		('ptype', 'uint32_t*', 'uint64_t'),
 419  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 420  
 421  	('task_for_pid', [
 422  		('target_tport', 'uint32_t'),
 423  		('pid', 'int32_t'),
 424  		('t', 'uint32_t*', 'uint64_t'),
 425  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX),
 426  
 427  	('task_name_for_pid', [
 428  		('target_tport', 'uint32_t'),
 429  		('pid', 'int32_t'),
 430  		('t', 'uint32_t*', 'uint64_t'),
 431  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX),
 432  
 433  	('pid_for_task', [
 434  		('t', 'uint32_t'),
 435  		('pid', 'int32_t*', 'uint64_t'),
 436  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX),
 437  
 438  	#
 439  	# Mach VM traps
 440  	#
 441  
 442  	('mach_vm_allocate', [
 443  		('target', 'uint32_t'),
 444  		('addr', 'uint64_t*', 'uint64_t'),
 445  		('size', 'uint64_t'),
 446  		('flags', 'int32_t'),
 447  	], [], XNU_TRAP_CALL),
 448  
 449  	('mach_vm_deallocate', [
 450  		('target', 'uint32_t'),
 451  		('address', 'uint64_t'),
 452  		('size', 'uint64_t'),
 453  	], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS),
 454  
 455  	#
 456  	# Mach semaphore traps
 457  	#
 458  
 459  	('semaphore_signal', [
 460  		('signal_name', 'uint32_t'),
 461  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX),
 462  
 463  	('semaphore_signal_all', [
 464  		('signal_name', 'uint32_t'),
 465  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX),
 466  
 467  	('semaphore_wait', [
 468  		('wait_name', 'uint32_t'),
 469  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS),
 470  
 471  	('semaphore_wait_signal', [
 472  		('wait_name', 'uint32_t'),
 473  		('signal_name', 'uint32_t'),
 474  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS),
 475  
 476  	('semaphore_timedwait', [
 477  		('wait_name', 'uint32_t'),
 478  		('sec', 'uint32_t'),
 479  		('nsec', 'uint32_t'),
 480  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS),
 481  
 482  	('semaphore_timedwait_signal', [
 483  		('wait_name', 'uint32_t'),
 484  		('signal_name', 'uint32_t'),
 485  		('sec', 'uint32_t'),
 486  		('nsec', 'uint32_t'),
 487  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS),
 488  
 489  	#
 490  	# mk_timer traps
 491  	#
 492  
 493  	('mk_timer_create', [], [
 494  		('port_name', 'uint32_t'),
 495  	]),
 496  
 497  	('mk_timer_destroy', [
 498  		('name', 'uint32_t'),
 499  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX),
 500  
 501  	('mk_timer_arm', [
 502  		('name', 'uint32_t'),
 503  		('expire_time', 'uint64_t'),
 504  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX),
 505  
 506  	('mk_timer_cancel', [
 507  		('name', 'uint32_t'),
 508  		('result_time', 'uint64_t*', 'uint64_t'),
 509  	], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX),
 510  
 511  	#
 512  	# psynch calls
 513  	#
 514  
 515  	('psynch_cvbroad', [
 516  		('cv', 'uint64_t'),
 517  		('cvlsgen', 'uint64_t'),
 518  		('cvudgen', 'uint64_t'),
 519  		('flags', 'uint32_t'),
 520  		('mutex', 'uint64_t'),
 521  		('mugen', 'uint64_t'),
 522  		('tid', 'uint64_t'),
 523  	], [
 524  		('retval', 'uint32_t'),
 525  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF),
 526  
 527  	('psynch_cvclrprepost', [
 528  		('cv', 'uint64_t'),
 529  		('cvgen', 'uint32_t'),
 530  		('cvugen', 'uint32_t'),
 531  		('cvsgen', 'uint32_t'),
 532  		('prepocnt', 'uint32_t'),
 533  		('preposeq', 'uint32_t'),
 534  		('flags', 'uint32_t'),
 535  	], [
 536  		('retval', 'uint32_t'),
 537  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF),
 538  
 539  	('psynch_cvsignal', [
 540  		('cv', 'uint64_t'),
 541  		('cvlsgen', 'uint64_t'),
 542  		('cvugen', 'uint32_t'),
 543  		('threadport', 'int32_t'),
 544  		('mutex', 'uint64_t'),
 545  		('mugen', 'uint64_t'),
 546  		('tid', 'uint64_t'),
 547  		('flags', 'uint32_t'),
 548  	], [
 549  		('retval', 'uint32_t'),
 550  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF),
 551  
 552  	('psynch_cvwait', [
 553  		('cv', 'uint64_t'),
 554  		('cvlsgen', 'uint64_t'),
 555  		('cvugen', 'uint32_t'),
 556  		('mutex', 'uint64_t'),
 557  		('mugen', 'uint64_t'),
 558  		('flags', 'uint32_t'),
 559  		('sec', 'int64_t'),
 560  		('nsec', 'uint32_t'),
 561  	], [
 562  		('retval', 'uint32_t'),
 563  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS),
 564  
 565  	('psynch_mutexdrop', [
 566  		('mutex', 'uint64_t'),
 567  		('mgen', 'uint32_t'),
 568  		('ugen', 'uint32_t'),
 569  		('tid', 'uint64_t'),
 570  		('flags', 'uint32_t'),
 571  	], [
 572  		('retval', 'uint32_t'),
 573  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF),
 574  
 575  	('psynch_mutexwait', [
 576  		('mutex', 'uint64_t'),
 577  		('mgen', 'uint32_t'),
 578  		('ugen', 'uint32_t'),
 579  		('tid', 'uint64_t'),
 580  		('flags', 'uint32_t'),
 581  	], [
 582  		('retval', 'uint32_t'),
 583  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS),
 584  
 585  	('psynch_rw_rdlock', [
 586  		('rwlock', 'uint64_t'),
 587  		('lgenval', 'uint32_t'),
 588  		('ugenval', 'uint32_t'),
 589  		('rw_wc', 'uint32_t'),
 590  		('flags', 'int32_t'),
 591  	], [
 592  		('retval', 'uint32_t'),
 593  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS),
 594  
 595  	('psynch_rw_unlock', [
 596  		('rwlock', 'uint64_t'),
 597  		('lgenval', 'uint32_t'),
 598  		('ugenval', 'uint32_t'),
 599  		('rw_wc', 'uint32_t'),
 600  		('flags', 'int32_t'),
 601  	], [
 602  		('retval', 'uint32_t'),
 603  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF),
 604  
 605  	('psynch_rw_wrlock', [
 606  		('rwlock', 'uint64_t'),
 607  		('lgenval', 'uint32_t'),
 608  		('ugenval', 'uint32_t'),
 609  		('rw_wc', 'uint32_t'),
 610  		('flags', 'int32_t'),
 611  	], [
 612  		('retval', 'uint32_t'),
 613  	], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS),
 614  
 615  	#
 616  	# debug calls
 617  	#
 618  
 619  	('debug_list_processes', [], [
 620  		('process_count', 'uint64_t'),
 621  		('fd', '@fd'),
 622  	], UNMANAGED_CALL),
 623  
 624  	('debug_list_ports', [
 625  		('process', 'uint32_t'),
 626  	], [
 627  		('port_count', 'uint64_t'),
 628  		('fd', '@fd'),
 629  	], UNMANAGED_CALL),
 630  
 631  	('debug_list_members', [
 632  		('process', 'uint32_t'),
 633  		('portset', 'uint32_t'),
 634  	], [
 635  		('port_count', 'uint64_t'),
 636  		('fd', '@fd'),
 637  	], UNMANAGED_CALL),
 638  
 639  	('debug_list_messages', [
 640  		('process', 'uint32_t'),
 641  		('port', 'uint32_t'),
 642  	], [
 643  		('message_count', 'uint64_t'),
 644  		('fd', '@fd'),
 645  	], UNMANAGED_CALL),
 646  ]
 647  
 648  ALLOWED_PRIVATE_TYPES = [
 649  	'bool',
 650  	'int8_t',
 651  	'uint8_t',
 652  	'int16_t',
 653  	'uint16_t',
 654  	'int32_t',
 655  	'uint32_t',
 656  	'int64_t',
 657  	'uint64_t',
 658  ]
 659  
 660  def parse_type(param_tuple, is_public):
 661  	type_str = param_tuple[1].strip()
 662  
 663  	if type_str == '@fd':
 664  		type_str = 'int' if is_public else 'int32_t'
 665  	else:
 666  		if not is_public and len(param_tuple) > 2:
 667  			type_str = param_tuple[2].strip()
 668  
 669  	if not is_public and type_str not in ALLOWED_PRIVATE_TYPES:
 670  		raise ValueError('Invalid private type: ' + type_str)
 671  
 672  	return type_str
 673  
 674  # we have to specify alignment for structures members greater than 4 bytes wide because on 32-bit architectures,
 675  # these are 4-byte aligned, but on 64-bit architectures, these are 8-byte aligned. however, we want the same structure
 676  # definitions across architectures, so we specify 8-byte alignment for 8-byte types.
 677  
 678  def alignment_for_type(type):
 679  	if type == 'int64_t' or type == 'uint64_t':
 680  		return 8
 681  	else:
 682  		return 0
 683  
 684  def alignment_str_for_type(type):
 685  	alignment = alignment_for_type(type)
 686  	if alignment > 0:
 687  		return ' __attribute__((aligned(' + str(alignment) + ')))'
 688  	else:
 689  		return ''
 690  
 691  def is_fd(param_tuple):
 692  	return param_tuple[1] == '@fd'
 693  
 694  if len(sys.argv) < 5:
 695  	sys.exit("Usage: " + sys.argv[0] + " <public-header-path> <internal-header-path> <library-source-path> <library-import>")
 696  
 697  os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True)
 698  os.makedirs(os.path.dirname(sys.argv[2]), exist_ok=True)
 699  os.makedirs(os.path.dirname(sys.argv[3]), exist_ok=True)
 700  
 701  def to_camel_case(snake_str):
 702  	components = snake_str.split('_')
 703  	return ''.join(x.title() for x in components)
 704  
 705  public_header = open(sys.argv[1], "w")
 706  internal_header = open(sys.argv[2], "w")
 707  library_source = open(sys.argv[3], "w")
 708  library_import = sys.argv[4]
 709  
 710  license_header = """\
 711  // This file has been auto-generated by generate-rpc-wrappers.py for use with darlingserver
 712  
 713  /**
 714   * This file is part of Darling.
 715   *
 716   * Copyright (C) {} Darling developers
 717   *
 718   * Darling is free software: you can redistribute it and/or modify
 719   * it under the terms of the GNU General Public License as published by
 720   * the Free Software Foundation, either version 3 of the License, or
 721   * (at your option) any later version.
 722   *
 723   * Darling is distributed in the hope that it will be useful,
 724   * but WITHOUT ANY WARRANTY; without even the implied warranty of
 725   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 726   * GNU General Public License for more details.
 727   *
 728   * You should have received a copy of the GNU General Public License
 729   * along with Darling.  If not, see <http://www.gnu.org/licenses/>.
 730   */
 731  
 732  """.format(datetime.now().year)
 733  
 734  public_header.write(license_header)
 735  library_source.write(license_header)
 736  internal_header.write(license_header)
 737  
 738  public_header.write("""\
 739  #ifndef _DARLINGSERVER_API_H_
 740  #define _DARLINGSERVER_API_H_
 741  
 742  #include <sys/types.h>
 743  #include <stdint.h>
 744  #include <stdbool.h>
 745  
 746  #ifdef __cplusplus
 747  extern "C" {
 748  #endif
 749  
 750  """)
 751  
 752  public_header.write("#define DSERVER_CALL_UNMANAGED_FLAG 0x80000000U\n\n")
 753  public_header.write("enum dserver_callnum {\n")
 754  # "52ccall" -> "s2c call"
 755  public_header.write("\tdserver_callnum_s2c = 0x52cca11,\n")
 756  public_header.write("\tdserver_callnum_push_reply = 0xbadca11,\n")
 757  public_header.write("\tdserver_callnum_invalid = 0,\n")
 758  idx = 1
 759  for call in calls:
 760  	call_name = call[0]
 761  	call_parameters = call[1]
 762  	reply_parameters = call[2]
 763  	flags = call[3] if len(call) >= 4 else 0
 764  
 765  	debug_flag = ""
 766  
 767  	if (flags & UNMANAGED_CALL) != 0:
 768  		debug_flag = "DSERVER_CALL_UNMANAGED_FLAG | "
 769  
 770  	public_header.write("\tdserver_callnum_" + call_name + " = " + debug_flag + str(idx) + "U,\n")
 771  
 772  	idx += 1
 773  public_header.write("};\n")
 774  
 775  public_header.write("""\
 776  
 777  typedef enum dserver_callnum dserver_callnum_t;
 778  
 779  #ifndef DSERVER_RPC_HOOKS_ARCHITECTURE
 780  #define DSERVER_RPC_HOOKS_ARCHITECTURE 1
 781  enum dserver_rpc_architecture {
 782  	dserver_rpc_architecture_invalid,
 783  	dserver_rpc_architecture_i386,
 784  	dserver_rpc_architecture_x86_64,
 785  	dserver_rpc_architecture_arm32,
 786  	dserver_rpc_architecture_arm64,
 787  };
 788  
 789  typedef enum dserver_rpc_architecture dserver_rpc_architecture_t;
 790  #endif
 791  
 792  typedef struct dserver_rpc_callhdr {
 793  	dserver_callnum_t number;
 794  	pid_t pid;
 795  	pid_t tid;
 796  	dserver_rpc_architecture_t architecture;
 797  } dserver_rpc_callhdr_t;
 798  
 799  typedef struct dserver_rpc_replyhdr {
 800  	dserver_callnum_t number;
 801  	int code;
 802  } dserver_rpc_replyhdr_t;
 803  
 804  typedef struct dserver_rpc_call_push_reply {
 805  	dserver_rpc_callhdr_t header;
 806  	uint64_t reply;
 807  	uint64_t reply_size;
 808  } dserver_rpc_call_push_reply_t;
 809  
 810  //
 811  // Debug calls
 812  //
 813  
 814  typedef struct dserver_debug_process {
 815  	uint32_t pid;
 816  	uint64_t port_count;
 817  } dserver_debug_process_t;
 818  
 819  typedef struct dserver_debug_port {
 820  	uint32_t port_name;
 821  	uint32_t rights;
 822  	uint64_t refs;
 823  	uint64_t messages;
 824  } dserver_debug_port_t;
 825  
 826  typedef struct dserver_debug_message {
 827  	uint32_t sender;
 828  	uint64_t size;
 829  } dserver_debug_message_t;
 830  
 831  """)
 832  
 833  library_source.write("""\
 834  #include {}
 835  #include <darlingserver/rpc-supplement.h>
 836  
 837  #if !defined(dserver_rpc_hooks_msghdr_t) || !defined(dserver_rpc_hooks_iovec_t) || !defined(dserver_rpc_hooks_cmsghdr_t) || !defined(DSERVER_RPC_HOOKS_CMSG_SPACE) || !defined(DSERVER_RPC_HOOKS_CMSG_FIRSTHDR) || !defined(DSERVER_RPC_HOOKS_SOL_SOCKET) || !defined(DSERVER_RPC_HOOKS_SCM_RIGHTS) || !defined(DSERVER_RPC_HOOKS_CMSG_LEN) || !defined(DSERVER_RPC_HOOKS_CMSG_DATA) || !defined(DSERVER_RPC_HOOKS_ATTRIBUTE) || !defined(dserver_rpc_hooks_atomic_save_t)
 838  	#error Missing definitions
 839  #endif
 840  
 841  #ifndef dserver_rpc_hooks_get_pid
 842  DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_pid(void);
 843  #endif
 844  
 845  #ifndef dserver_rpc_hooks_get_tid
 846  DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_tid(void);
 847  #endif
 848  
 849  #ifndef dserver_rpc_hooks_get_architecture
 850  DSERVER_RPC_HOOKS_ATTRIBUTE dserver_rpc_architecture_t dserver_rpc_hooks_get_architecture(void);
 851  #endif
 852  
 853  #ifndef dserver_rpc_hooks_get_server_address
 854  DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_get_server_address(void);
 855  #endif
 856  
 857  #ifndef dserver_rpc_hooks_get_server_address_length
 858  DSERVER_RPC_HOOKS_ATTRIBUTE size_t dserver_rpc_hooks_get_server_address_length(void);
 859  #endif
 860  
 861  #ifndef dserver_rpc_hooks_memcpy
 862  DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_memcpy(void* destination, const void* source, size_t length);
 863  #endif
 864  
 865  #ifndef dserver_rpc_hooks_send_message
 866  DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message);
 867  #endif
 868  
 869  #ifndef dserver_rpc_hooks_receive_message
 870  DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message);
 871  #endif
 872  
 873  #ifndef dserver_rpc_hooks_get_bad_message_status
 874  DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_bad_message_status(void);
 875  #endif
 876  
 877  #ifndef dserver_rpc_hooks_get_communication_error_status
 878  DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_communication_error_status(void);
 879  #endif
 880  
 881  #ifndef dserver_rpc_hooks_get_broken_pipe_status
 882  DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_broken_pipe_status(void);
 883  #endif
 884  
 885  #ifndef dserver_rpc_hooks_close_fd
 886  DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_close_fd(int fd);
 887  #endif
 888  
 889  #ifndef dserver_rpc_hooks_get_socket
 890  DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_socket(void);
 891  #endif
 892  
 893  #ifndef dserver_rpc_hooks_printf
 894  DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_printf(const char* format, ...);
 895  #endif
 896  
 897  #ifndef dserver_rpc_hooks_atomic_begin
 898  DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_atomic_begin(dserver_rpc_hooks_atomic_save_t* atomic_save);
 899  #endif
 900  
 901  #ifndef dserver_rpc_hooks_atomic_end
 902  DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_atomic_end(dserver_rpc_hooks_atomic_save_t* atomic_save);
 903  #endif
 904  
 905  #ifndef dserver_rpc_hooks_get_interrupt_status
 906  DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_interrupt_status(void);
 907  #endif
 908  
 909  #ifndef dserver_rpc_hooks_push_reply
 910  DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_push_reply(int socket, const dserver_rpc_hooks_msghdr_t* message, size_t size);
 911  #endif
 912  
 913  """.format(library_import))
 914  
 915  internal_header.write("#define DSERVER_VALID_CALLNUM_CASES \\\n")
 916  for call in calls:
 917  	call_name = call[0]
 918  	call_parameters = call[1]
 919  	reply_parameters = call[2]
 920  
 921  	internal_header.write("\tcase dserver_callnum_" + call_name + ": \\\n")
 922  internal_header.write("\n")
 923  
 924  internal_header.write("#define DSERVER_CONSTRUCT_CASES \\\n")
 925  for call in calls:
 926  	call_name = call[0]
 927  	call_parameters = call[1]
 928  	reply_parameters = call[2]
 929  	camel_name = to_camel_case(call_name)
 930  
 931  	internal_header.write("\tCALL_CASE(" + call_name + ", " + camel_name + "); \\\n")
 932  internal_header.write("\n")
 933  
 934  internal_header.write("#define DSERVER_ENUM_VALUES \\\n")
 935  for call in calls:
 936  	call_name = call[0]
 937  	call_parameters = call[1]
 938  	reply_parameters = call[2]
 939  	camel_name = to_camel_case(call_name)
 940  
 941  	internal_header.write("\t" + camel_name + " = dserver_callnum_" + call_name + ", \\\n")
 942  internal_header.write("\n")
 943  
 944  internal_header.write("#define DSERVER_CLASS_DECLS \\\n")
 945  for call in calls:
 946  	call_name = call[0]
 947  	call_parameters = call[1]
 948  	reply_parameters = call[2]
 949  	camel_name = to_camel_case(call_name)
 950  
 951  	internal_header.write("\tclass " + camel_name + "; \\\n")
 952  internal_header.write("\n")
 953  
 954  internal_header.write("#define DSERVER_CLASS_DEFS \\\n")
 955  for call in calls:
 956  	call_name = call[0]
 957  	call_parameters = call[1]
 958  	reply_parameters = call[2]
 959  	flags = call[3] if len(call) >= 4 else 0
 960  	camel_name = to_camel_case(call_name)
 961  	fd_count_in_reply = 0
 962  
 963  	internal_header.write(textwrap.indent(textwrap.dedent("""\
 964  		class Call::{1}: public Call, public std::enable_shared_from_this<Call::{1}> {{ \\
 965  			friend class Call; \\
 966  		private: \\
 967  			{2}
 968  		public: \\
 969  			{1}(std::shared_ptr<Thread> thread, dserver_rpc_call_{0}_t* data, Message&& requestMessage): \\
 970  				Call(thread, requestMessage.address(), reinterpret_cast<dserver_rpc_callhdr_t*>(data)){3} \\
 971  				{4}
 972  			{{ \\
 973  		"""), '\t').format(
 974  			call_name,
 975  			camel_name,
 976  			("dserver_call_" + call_name + "_t _body; \\") if len(call_parameters) > 0 else "\\",
 977  			"," if len(call_parameters) > 0 else "",
 978  			"_body(data->body) \\" if len(call_parameters) > 0 else "\\"
 979  		)
 980  	)
 981  
 982  	for param in call_parameters:
 983  		param_name = param[0]
 984  
 985  		if not is_fd(param):
 986  			continue
 987  
 988  		internal_header.write("\t\t\t_body." + param_name + " = requestMessage.extractDescriptorAtIndex(_body." + param_name + "); \\\n")
 989  	internal_header.write("\t\t}; \\\n")
 990  
 991  	internal_header.write("\t\t~" + camel_name + "() { \\\n")
 992  	for param in call_parameters:
 993  		param_name = param[0]
 994  
 995  		if not is_fd(param):
 996  			continue
 997  
 998  		internal_header.write("\t\t\tif (_body." + param_name + " != -1) { \\\n")
 999  		internal_header.write("\t\t\t\tclose(_body." + param_name + "); \\\n")
1000  		internal_header.write("\t\t} \\\n")
1001  
1002  	internal_header.write(textwrap.indent(textwrap.dedent("""\
1003  			}}; \\
1004  			virtual Call::Number number() const override {{ \\
1005  				return Call::Number::{0}; \\
1006  			}}; \\
1007  			virtual void processCall() override; \\
1008  		private: \\
1009  		"""), '\t').format(camel_name))
1010  
1011  	internal_header.write("\t\tvoid _sendReply(int resultCode")
1012  	for param in reply_parameters:
1013  		param_name = param[0]
1014  
1015  		if is_fd(param):
1016  			fd_count_in_reply += 1
1017  
1018  		internal_header.write(", " + parse_type(param, False) + " " + param_name)
1019  	internal_header.write(") { \\\n")
1020  
1021  	internal_header.write("\t\t\trpcReplyLog.debug() << \"Replying to call #\" << dserver_callnum_" + call_name + " << \" (dserver_callnum_" + call_name + ") from PID \" << _header.pid << \", TID \" << _header.tid << \" with result code \" << resultCode ")
1022  
1023  	for param in reply_parameters:
1024  		param_name = param[0]
1025  
1026  		internal_header.write("<< \", " + param_name + "=\" << " + param_name + " ")
1027  
1028  	internal_header.write("<< rpcReplyLog.endLog; \\\n")
1029  
1030  	internal_header.write(textwrap.indent(textwrap.dedent("""\
1031  		Message reply(sizeof(dserver_rpc_reply_{0}_t), 0); \\
1032  		int fdIndex = 0; \\
1033  		reply.setAddress(_replyAddress); \\
1034  		auto replyStruct = reinterpret_cast<dserver_rpc_reply_{0}_t*>(reply.data().data()); \\
1035  		replyStruct->header.number = dserver_callnum_{0}; \\
1036  		replyStruct->header.code = resultCode; \\
1037  		"""), '\t\t\t').format(call_name))
1038  
1039  	fd_index = 0
1040  	for param in reply_parameters:
1041  		param_name = param[0]
1042  		val = param_name
1043  
1044  		if is_fd(param):
1045  			val = "((" + param_name + " >= 0) ? (fdIndex++) : (-1))"
1046  			internal_header.write("\t\t\tif (" + param_name + " >= 0) { \\\n")
1047  			internal_header.write("\t\t\t\treply.pushDescriptor(" + param_name + "); \\\n")
1048  			internal_header.write("\t\t\t} \\\n")
1049  
1050  		internal_header.write("\t\t\treplyStruct->body." + param_name + " = " + val + "; \\\n")
1051  	internal_header.write("\t\t\tif (auto thread = _thread.lock()) { \\\n")
1052  	internal_header.write("\t\t\t\tthread->pushCallReply(shared_from_this(), std::move(reply)); \\\n")
1053  	internal_header.write("\t\t\t} else { \\\n")
1054  	internal_header.write("\t\t\t\tCall::sendReply(std::move(reply)); \\\n")
1055  	internal_header.write("\t\t\t} \\\n")
1056  	internal_header.write("\t\t}; \\\n")
1057  
1058  	if len(reply_parameters) == 0:
1059  		internal_header.write("\tpublic: \\\n")
1060  		internal_header.write("\t\tvoid sendBasicReply(int resultCode) override { \\\n")
1061  		internal_header.write("\t\t\t_sendReply(resultCode); \\\n")
1062  		internal_header.write("\t\t}; \\\n")
1063  
1064  	if (flags & XNU_TRAP_CALL) != 0:
1065  		internal_header.write("\t\tbool isXNUTrap() const override { \\\n")
1066  		internal_header.write("\t\t\treturn true; \\\n")
1067  		internal_header.write("\t\t}; \\\n")
1068  
1069  	if (flags & XNU_TRAP_BSD) != 0:
1070  		internal_header.write("\t\tbool isBSDTrap() const override { \\\n")
1071  		internal_header.write("\t\t\treturn true; \\\n")
1072  		internal_header.write("\t\t}; \\\n")
1073  		internal_header.write("\t\tvoid sendBSDReply(int resultCode, uint32_t returnValue) override { \\\n")
1074  		internal_header.write("\t\t\t_sendReply(resultCode, returnValue); \\\n")
1075  		internal_header.write("\t\t}; \\\n")
1076  
1077  	internal_header.write("\t}; \\\n")
1078  internal_header.write("\n")
1079  
1080  internal_header.write("#define DSERVER_CLASS_SOURCE_DEFS \\\n")
1081  for call in calls:
1082  	call_name = call[0]
1083  	call_parameters = call[1]
1084  	reply_parameters = call[2]
1085  	flags = call[3] if len(call) >= 4 else 0
1086  	camel_name = to_camel_case(call_name)
1087  
1088  	if (flags & XNU_TRAP_CALL) == 0:
1089  		continue
1090  
1091  	# XNU traps return values by writing directly to the calling process's memory at the addresses given as regular call parameters
1092  	if (flags & XNU_TRAP_BSD) != 0:
1093  		if len(reply_parameters) != 1:
1094  			raise RuntimeError("Call marked as a BSD trap does not have exactly 1 reply parameter")
1095  	elif len(reply_parameters) > 0:
1096  		raise RuntimeError("Call marked as an XNU trap has reply parameters")
1097  
1098  	internal_header.write("\tvoid DarlingServer::Call::{0}::processCall() {{ \\\n".format(camel_name))
1099  
1100  	if (flags & XNU_TRAP_BSD) != 0:
1101  		internal_header.write("\t\tuint32_t* retvalPointer = nullptr; \\\n")
1102  		internal_header.write("\t\t{ \\\n")
1103  		internal_header.write("\t\t\tif (auto thread = _thread.lock()) { \\\n")
1104  		internal_header.write("\t\t\t\tretvalPointer = thread->bsdReturnValuePointer(); \\\n")
1105  		internal_header.write("\t\t\t} \\\n")
1106  		internal_header.write("\t\t}; \\\n")
1107  
1108  	internal_header.write("\t\tThread::syscallReturn(dtape_{0}(".format(call_name))
1109  
1110  	is_first = True
1111  	for param in call_parameters:
1112  		param_name = param[0]
1113  
1114  		if is_first:
1115  			is_first = False
1116  		else:
1117  			internal_header.write(", ")
1118  
1119  		internal_header.write("_body.{0}".format(param_name))
1120  
1121  	if (flags & XNU_TRAP_BSD) != 0:
1122  		if not is_first:
1123  			internal_header.write(", ")
1124  		internal_header.write("retvalPointer")
1125  
1126  	internal_header.write(")); \\\n")
1127  	internal_header.write("\t}; \\\n")
1128  internal_header.write("\n")
1129  
1130  internal_header.write("#define DSERVER_DTAPE_DECLS \\\n")
1131  for call in calls:
1132  	call_name = call[0]
1133  	call_parameters = call[1]
1134  	flags = call[3] if len(call) >= 4 else 0
1135  	camel_name = to_camel_case(call_name)
1136  
1137  	if (flags & XNU_TRAP_CALL) == 0:
1138  		continue
1139  
1140  	internal_header.write("\tint dtape_{0}(".format(call_name))
1141  
1142  	is_first = True
1143  	for param in call_parameters:
1144  		param_name = param[0]
1145  
1146  		if is_first:
1147  			is_first = False
1148  		else:
1149  			internal_header.write(", ")
1150  
1151  		internal_header.write("{0} {1}".format(parse_type(param, False), param_name))
1152  
1153  	if (flags & XNU_TRAP_BSD) != 0:
1154  		if not is_first:
1155  			internal_header.write(", ")
1156  		internal_header.write("uint32_t* retval")
1157  
1158  	internal_header.write("); \\\n")
1159  internal_header.write("\n")
1160  
1161  internal_header.write("#define DSERVER_DTAPE_DEFS \\\n")
1162  for call in calls:
1163  	call_name = call[0]
1164  	call_parameters = call[1]
1165  	flags = call[3] if len(call) >= 4 else 0
1166  	camel_name = to_camel_case(call_name)
1167  
1168  	if (flags & XNU_TRAP_CALL) == 0 or (flags & XNU_TRAP_NO_DTAPE_DEF) != 0:
1169  		continue
1170  
1171  	trap_name = call_name
1172  	if (flags & XNU_TRAP_NOPREFIX) == 0:
1173  		trap_name = "_kernelrpc_" + trap_name
1174  	if (flags & XNU_TRAP_NOSUFFIX) == 0:
1175  		trap_name = trap_name + "_trap"
1176  
1177  	trap_args_name = call_name
1178  	if (flags & XNU_TRAP_NOPREFIX) == 0:
1179  		trap_args_name = "_kernelrpc_" + trap_args_name
1180  	if (flags & (XNU_TRAP_NOSUFFIX | XNU_TRAP_NOSUFFIX_ARGS)) == 0:
1181  		trap_args_name = trap_args_name + "_trap"
1182  
1183  	internal_header.write("\tint dtape_{0}(".format(call_name))
1184  
1185  	is_first = True
1186  	for param in call_parameters:
1187  		param_name = param[0]
1188  
1189  		if is_first:
1190  			is_first = False
1191  		else:
1192  			internal_header.write(", ")
1193  
1194  		internal_header.write("{0} {1}".format(parse_type(param, False), param_name))
1195  
1196  	internal_header.write(") { \\\n")
1197  	internal_header.write("\t\tstruct {0}_args args = {{ \\\n".format(trap_args_name))
1198  
1199  	for param in call_parameters:
1200  		param_name = param[0]
1201  		internal_header.write("\t\t\t.{0} = {0}, \\\n".format(param_name))
1202  
1203  	internal_header.write("\t\t}; \\\n")
1204  	internal_header.write("\t\treturn {0}(&args); \\\n".format(trap_name))
1205  	internal_header.write("\t}; \\\n")
1206  internal_header.write("\n")
1207  
1208  public_header.write("__attribute__((always_inline)) static const char* dserver_callnum_to_string(dserver_callnum_t callnum) {\n")
1209  public_header.write("\tswitch (callnum) {\n")
1210  public_header.write("\t\tcase dserver_callnum_s2c: return \"dserver_callnum_s2c\";\n")
1211  public_header.write("\t\tcase dserver_callnum_push_reply: return \"dserver_callnum_push_reply\";\n")
1212  for call in calls:
1213  	call_name = call[0]
1214  	public_header.write("\t\tcase dserver_callnum_" + call_name + ": return \"dserver_callnum_" + call_name + "\";\n")
1215  public_header.write("\t\tdefault: return (const char*)0;\n")
1216  public_header.write("\t}\n")
1217  public_header.write("};\n\n")
1218  
1219  max_call_fd_count = 0
1220  max_reply_fd_count = 0
1221  for call in calls:
1222  	call_name = call[0]
1223  	call_parameters = call[1]
1224  	reply_parameters = call[2]
1225  	fd_count_in_call = 0
1226  	fd_count_in_reply = 0
1227  	flags = call[3] if len(call) >= 4 else 0
1228  	for param in call_parameters:
1229  		if is_fd(param):
1230  			fd_count_in_call += 1
1231  	for param in reply_parameters:
1232  		if is_fd(param):
1233  			fd_count_in_reply += 1
1234  	if fd_count_in_call > max_call_fd_count:
1235  		max_call_fd_count = fd_count_in_call
1236  	if fd_count_in_reply > max_reply_fd_count:
1237  		max_reply_fd_count = fd_count_in_reply
1238  
1239  for call in calls:
1240  	call_name = call[0]
1241  	call_parameters = call[1]
1242  	reply_parameters = call[2]
1243  	fd_count_in_call = 0
1244  	fd_count_in_reply = 0
1245  	flags = call[3] if len(call) >= 4 else 0
1246  
1247  	# define the RPC call body structure
1248  	if len(call_parameters) > 0:
1249  		public_header.write("typedef struct dserver_call_" + call_name + " dserver_call_" + call_name + "_t;\n")
1250  		public_header.write("struct dserver_call_" + call_name + " {\n")
1251  		for param in call_parameters:
1252  			param_name = param[0]
1253  
1254  			if is_fd(param):
1255  				fd_count_in_call += 1
1256  
1257  			parsed_type = parse_type(param, False)
1258  			public_header.write("\t" + parsed_type + " " + param_name + alignment_str_for_type(parsed_type) + ";\n")
1259  		public_header.write("};\n")
1260  
1261  	# define the RPC call structure
1262  	public_header.write(textwrap.dedent("""\
1263  		typedef struct dserver_rpc_call_{0} dserver_rpc_call_{0}_t;
1264  		struct dserver_rpc_call_{0} {{
1265  			dserver_rpc_callhdr_t header;
1266  		""").format(call_name))
1267  	if len(call_parameters) > 0:
1268  		public_header.write("\tdserver_call_" + call_name + "_t body;\n")
1269  	public_header.write("};\n")
1270  
1271  	# define the RPC reply body structure
1272  	if len(reply_parameters) > 0:
1273  		public_header.write("typedef struct dserver_reply_" + call_name + " dserver_reply_" + call_name + "_t;\n")
1274  		public_header.write("struct dserver_reply_" + call_name + " {\n")
1275  		for param in reply_parameters:
1276  			param_name = param[0]
1277  
1278  			if is_fd(param):
1279  				fd_count_in_reply += 1
1280  
1281  			parsed_type = parse_type(param, False)
1282  			public_header.write("\t" + parsed_type + " " + param_name + alignment_str_for_type(parsed_type) + ";\n")
1283  		public_header.write("};\n")
1284  
1285  	# define the RPC reply structure
1286  	public_header.write(textwrap.dedent("""\
1287  		typedef struct dserver_rpc_reply_{0} dserver_rpc_reply_{0}_t;
1288  		struct dserver_rpc_reply_{0} {{
1289  			dserver_rpc_replyhdr_t header;
1290  		""").format(call_name))
1291  	if len(reply_parameters) > 0:
1292  		public_header.write("\tdserver_reply_" + call_name + "_t body;\n")
1293  	public_header.write("};\n")
1294  
1295  	tmp = "int dserver_rpc_explicit_" + call_name + "(int server_socket"
1296  	public_header.write(tmp)
1297  	library_source.write(tmp)
1298  	for param in call_parameters:
1299  		param_name = param[0]
1300  
1301  		tmp = ", "
1302  		tmp += parse_type(param, True) + " " + param_name
1303  		public_header.write(tmp)
1304  		library_source.write(tmp)
1305  
1306  	for param in reply_parameters:
1307  		param_name = param[0]
1308  
1309  		tmp = ", "
1310  		tmp += parse_type(param, True) + "* out_" + param_name
1311  		public_header.write(tmp)
1312  		library_source.write(tmp)
1313  	public_header.write(");\n\n")
1314  	library_source.write(") {\n")
1315  
1316  	# define the RPC call wrapper function
1317  	library_source.write(textwrap.indent(textwrap.dedent("""\
1318  		dserver_rpc_call_{0}_t call = {{
1319  			.header = {{
1320  				.architecture = dserver_rpc_hooks_get_architecture(),
1321  				.pid = dserver_rpc_hooks_get_pid(),
1322  				.tid = dserver_rpc_hooks_get_tid(),
1323  				.number = dserver_callnum_{0},
1324  			}},
1325  		"""), '\t').format(call_name))
1326  
1327  	if len(call_parameters) > 0:
1328  		library_source.write("\t\t.body = {\n")
1329  		fd_index = 0
1330  		for param in call_parameters:
1331  			param_name = param[0]
1332  			val = param_name
1333  
1334  			if is_fd(param):
1335  				val = "(" + param_name + " < 0) ? -1 : " + str(fd_index)
1336  				fd_index += 1
1337  
1338  			# On recent versions of clang, the compiler will throw out an error if there is
1339  			# a mismatch with certain integer types. 
1340  			if len(param) > 2: 
1341  				val = "(" + param[2] + ")" + val
1342  
1343  			library_source.write("\t\t\t." + param_name + " = " + val + ",\n")
1344  		library_source.write("\t\t},\n")
1345  
1346  	library_source.write("\t};\n")
1347  	library_source.write("\tunion {\n")
1348  	library_source.write("\t\tdserver_rpc_reply_" + call_name + "_t reply;\n")
1349  	library_source.write("\t\tdserver_s2c_call_t s2c;\n")
1350  
1351  	# make room for any potetial replies if we need to handle unexpected replies
1352  	if (flags & PUSH_UNKNOWN_REPLIES) != 0:
1353  		for other_call in calls:
1354  			other_call_name = other_call[0]
1355  			other_call_parameters = other_call[1]
1356  			other_reply_parameters = other_call[2]
1357  			other_fd_count_in_reply = 0
1358  			other_flags = other_call[3] if len(other_call) >= 4 else 0
1359  			library_source.write("\t\tdserver_rpc_reply_" + other_call_name + "_t potential_reply_" + other_call_name + ";\n")
1360  
1361  	library_source.write("\t} reply_msg;\n")
1362  
1363  	# we always allocate space for at least one FD, since S2C calls might send a descriptor
1364  	library_source.write("\tint fds[" + str(max(1, fd_count_in_call, fd_count_in_reply, max_reply_fd_count if (flags & PUSH_UNKNOWN_REPLIES) != 0 else 0)) + "];\n")
1365  	library_source.write("\tint valid_fd_count;\n")
1366  	library_source.write("\tchar controlbuf[DSERVER_RPC_HOOKS_CMSG_SPACE(sizeof(fds))];\n")
1367  
1368  	if fd_count_in_call > 0:
1369  		library_source.write("\tvalid_fd_count = 0;\n")
1370  		for param in call_parameters:
1371  			param_name = param[0]
1372  
1373  			if not is_fd(param):
1374  				continue
1375  
1376  			library_source.write("\tif (" + param_name + " >= 0) {\n")
1377  			library_source.write("\t\tfds[valid_fd_count++] = " + param_name + ";\n")
1378  			library_source.write("\t}\n")
1379  
1380  	library_source.write(textwrap.indent(textwrap.dedent("""\
1381  		dserver_rpc_hooks_iovec_t call_data = {
1382  			.iov_base = &call,
1383  			.iov_len = sizeof(call),
1384  		};
1385  		dserver_rpc_hooks_msghdr_t callmsg = {
1386  			.msg_name = dserver_rpc_hooks_get_server_address(),
1387  			.msg_namelen = dserver_rpc_hooks_get_server_address_length(),
1388  			.msg_iov = &call_data,
1389  			.msg_iovlen = 1,
1390  		"""), '\t'))
1391  
1392  	if fd_count_in_call == 0:
1393  		library_source.write("\t\t.msg_control = NULL,\n")
1394  		library_source.write("\t\t.msg_controllen = 0,\n")
1395  	else:
1396  		library_source.write("\t\t.msg_control = (valid_fd_count > 0) ? controlbuf : NULL,\n")
1397  		library_source.write("\t\t.msg_controllen = (valid_fd_count > 0) ? sizeof(controlbuf) : 0,\n")
1398  
1399  	library_source.write("\t};\n")
1400  
1401  	if fd_count_in_call > 0:
1402  		library_source.write(textwrap.indent(textwrap.dedent("""\
1403  			if (valid_fd_count > 0) {
1404  				dserver_rpc_hooks_cmsghdr_t* call_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&callmsg);
1405  				call_cmsg->cmsg_level = DSERVER_RPC_HOOKS_SOL_SOCKET;
1406  				call_cmsg->cmsg_type = DSERVER_RPC_HOOKS_SCM_RIGHTS;
1407  				call_cmsg->cmsg_len = DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * valid_fd_count);
1408  				dserver_rpc_hooks_memcpy(DSERVER_RPC_HOOKS_CMSG_DATA(call_cmsg), fds, sizeof(int) * valid_fd_count);
1409  			}
1410  			"""), '\t'))
1411  
1412  	library_source.write(textwrap.indent(textwrap.dedent("""\
1413  		dserver_rpc_hooks_iovec_t reply_data = {
1414  			.iov_base = &reply_msg,
1415  			.iov_len = sizeof(reply_msg),
1416  		};
1417  		dserver_rpc_hooks_msghdr_t replymsg = {
1418  			.msg_name = NULL,
1419  			.msg_namelen = 0,
1420  			.msg_iov = &reply_data,
1421  			.msg_iovlen = 1,
1422  			.msg_control = controlbuf,
1423  			.msg_controllen = sizeof(controlbuf),
1424  		"""), '\t'))
1425  
1426  	library_source.write("\t};\n\n")
1427  
1428  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1429  		library_source.write("\tdserver_rpc_hooks_atomic_save_t atomic_save;\n")
1430  		library_source.write("\tdserver_rpc_hooks_atomic_begin(&atomic_save);\n\n")
1431  
1432  	library_source.write("\tlong int long_status;\n\n")
1433  
1434  	library_source.write("retry_send:\n")
1435  	library_source.write("\tlong_status = dserver_rpc_hooks_send_message(server_socket, &callmsg);\n\n")
1436  
1437  	library_source.write("\tif (long_status == dserver_rpc_hooks_get_interrupt_status()) {\n")
1438  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1439  		library_source.write("\t\tgoto retry_send;\n")
1440  	else:
1441  		library_source.write("\t\treturn (int)long_status;\n")
1442  	library_source.write("\t}\n\n")
1443  
1444  	library_source.write("\tif (long_status < 0) {\n")
1445  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1446  		library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n")
1447  	library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD SEND STATUS: %ld ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status);\n")
1448  	library_source.write("\t\treturn (int)long_status;\n")
1449  	library_source.write("\t}\n\n")
1450  
1451  	library_source.write("\tif (long_status != sizeof(call)) {\n")
1452  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1453  		library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n")
1454  	library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD SEND LENGTH: %ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status, sizeof(call));\n")
1455  	library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
1456  	library_source.write("\t}\n\n")
1457  
1458  	library_source.write("retry_receive:\n")
1459  	library_source.write("\tlong_status = dserver_rpc_hooks_receive_message(server_socket, &replymsg);\n\n")
1460  
1461  	library_source.write("\tif (long_status == dserver_rpc_hooks_get_interrupt_status()) {\n")
1462  	library_source.write("\t\tgoto retry_receive;\n")
1463  	library_source.write("\t}\n\n")
1464  	
1465  	library_source.write("\tif (long_status < 0) {\n")
1466  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1467  		library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n")
1468  	library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE STATUS: %ld ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status);\n")
1469  	library_source.write("\t\treturn (int)long_status;\n")
1470  	library_source.write("\t}\n\n")
1471  
1472  	library_source.write("\tif (long_status < sizeof(dserver_rpc_replyhdr_t)) {\n")
1473  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1474  		library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n")
1475  	library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE MESSAGE: length=%ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status, sizeof(reply_msg.reply));\n")
1476  	library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
1477  	library_source.write("\t}\n\n")
1478  
1479  	library_source.write("\tif (reply_msg.reply.header.number != dserver_callnum_" + call_name + ") {\n")
1480  	if (flags & PUSH_UNKNOWN_REPLIES) != 0:
1481  		library_source.write("\t\tdserver_rpc_hooks_push_reply(server_socket, &replymsg, long_status);\n")
1482  		library_source.write("\t\tgoto retry_receive;\n")
1483  	else:
1484  		if (flags & ALLOW_INTERRUPTIONS) == 0:
1485  			library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n")
1486  		library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE MESSAGE: number=%d (expected %d), code=%d, length=%ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, reply_msg.reply.header.number, dserver_callnum_" + call_name + ", reply_msg.reply.header.code, long_status, sizeof(reply_msg.reply));\n")
1487  		library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
1488  	library_source.write("\t}\n\n")
1489  
1490  	library_source.write("\tif (long_status != sizeof(reply_msg.reply)) {\n")
1491  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1492  		library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n")
1493  	library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE MESSAGE: number=%d (expected %d), code=%d, length=%ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, reply_msg.reply.header.number, dserver_callnum_" + call_name + ", reply_msg.reply.header.code, long_status, sizeof(reply_msg.reply));\n")
1494  	library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
1495  	library_source.write("\t}\n\n")
1496  
1497  	if (flags & ALLOW_INTERRUPTIONS) == 0:
1498  		library_source.write("\tdserver_rpc_hooks_atomic_end(&atomic_save);\n\n")
1499  
1500  	if fd_count_in_reply != 0:
1501  		library_source.write("\tvalid_fd_count = 0;\n")
1502  		for param in reply_parameters:
1503  			param_name = param[0]
1504  
1505  			if not is_fd(param):
1506  				continue
1507  
1508  			library_source.write("\tif (reply_msg.reply.body." + param_name + " >= 0) {\n")
1509  			library_source.write("\t\t++valid_fd_count;\n")
1510  			library_source.write("\t}\n")
1511  
1512  		library_source.write(textwrap.indent(textwrap.dedent("""\
1513  			if (valid_fd_count > 0) {
1514  				dserver_rpc_hooks_cmsghdr_t* reply_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&replymsg);
1515  				if (!reply_cmsg || reply_cmsg->cmsg_level != DSERVER_RPC_HOOKS_SOL_SOCKET || reply_cmsg->cmsg_type != DSERVER_RPC_HOOKS_SCM_RIGHTS || reply_cmsg->cmsg_len != DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * valid_fd_count)) {
1516  					return dserver_rpc_hooks_get_bad_message_status();
1517  				}
1518  				dserver_rpc_hooks_memcpy(fds, DSERVER_RPC_HOOKS_CMSG_DATA(reply_cmsg), sizeof(int) * valid_fd_count);
1519  			}
1520  			"""), '\t'))
1521  
1522  	for param in reply_parameters:
1523  		param_name = param[0]
1524  
1525  		if is_fd(param):
1526  			library_source.write("\tif (out_" + param_name + ") {\n")
1527  			library_source.write("\t\t*out_" + param_name + " = (reply_msg.reply.body." + param_name + " >= 0) ? fds[reply_msg.reply.body." + param_name + "] : -1;\n")
1528  			library_source.write("\t} else if (reply_msg.reply.body." + param_name + " >= 0) {\n")
1529  			library_source.write("\t\tdserver_rpc_hooks_close_fd(fds[reply_msg.reply.body." + param_name + "]);\n")
1530  			library_source.write("\t}\n")
1531  		else:
1532  			library_source.write("\tif (out_" + param_name + ") {\n")
1533  			library_source.write("\t\t*out_" + param_name + " = reply_msg.reply.body." + param_name + ";\n")
1534  			library_source.write("\t}\n")
1535  
1536  	library_source.write("\treturn reply_msg.reply.header.code;\n")
1537  
1538  	library_source.write("};\n\n")
1539  
1540  	# declare the RPC call wrapper function
1541  	# (and output the prototype part of the function definition)
1542  	tmp = "int dserver_rpc_" + call_name + "("
1543  	public_header.write(tmp)
1544  	library_source.write(tmp)
1545  	is_first = True
1546  	for param in call_parameters:
1547  		param_name = param[0]
1548  
1549  		if is_first:
1550  			is_first = False
1551  			tmp = ""
1552  		else:
1553  			tmp = ", "
1554  		tmp += parse_type(param, True) + " " + param_name
1555  		public_header.write(tmp)
1556  		library_source.write(tmp)
1557  
1558  	for param in reply_parameters:
1559  		param_name = param[0]
1560  
1561  		if is_first:
1562  			is_first = False
1563  			tmp = ""
1564  		else:
1565  			tmp = ", "
1566  		tmp += parse_type(param, True) + "* out_" + param_name
1567  		public_header.write(tmp)
1568  		library_source.write(tmp)
1569  	public_header.write(");\n\n")
1570  	library_source.write(") {\n")
1571  
1572  	library_source.write("\tint server_socket = dserver_rpc_hooks_get_socket();\n")
1573  	library_source.write("\tif (server_socket < 0) {\n")
1574  	library_source.write("\t\treturn dserver_rpc_hooks_get_broken_pipe_status();\n")
1575  	library_source.write("\t}\n\n")
1576  
1577  	library_source.write("\treturn dserver_rpc_explicit_" + call_name + "(server_socket")
1578  
1579  	for param in call_parameters:
1580  		param_name = param[0]
1581  
1582  		tmp = ", "
1583  		tmp += param_name
1584  		library_source.write(tmp)
1585  
1586  	for param in reply_parameters:
1587  		param_name = param[0]
1588  
1589  		tmp = ", "
1590  		tmp += "out_" + param_name
1591  		library_source.write(tmp)
1592  
1593  	library_source.write(");\n")
1594  
1595  	library_source.write("};\n\n")
1596  
1597  public_header.write("// we don't care about multiple evaluation here\n")
1598  public_header.write("#define dserver_rpc_helper_max(a, b) (((b) > (a)) ? (b) : (a))\n\n")
1599  
1600  curr_call_len_str = "0"
1601  curr_reply_len_str = "0"
1602  for call in calls:
1603  	call_name = call[0]
1604  	call_parameters = call[1]
1605  	reply_parameters = call[2]
1606  	flags = call[3] if len(call) >= 4 else 0
1607  	curr_call_len_str = "(dserver_rpc_helper_max(sizeof(dserver_rpc_call_" + call_name + "_t), " + curr_call_len_str + "))"
1608  	curr_reply_len_str = "(dserver_rpc_helper_max(sizeof(dserver_rpc_reply_" + call_name + "_t), " + curr_reply_len_str + "))"
1609  
1610  public_header.write("#define DSERVER_RPC_CALL_MAX_LENGTH " + curr_call_len_str + "\n")
1611  public_header.write("#define DSERVER_RPC_REPLY_MAX_LENGTH " + curr_reply_len_str + "\n")
1612  public_header.write("#define DSERVER_RPC_CALL_MAX_FD_COUNT " + str(max_call_fd_count) + "\n")
1613  public_header.write("#define DSERVER_RPC_REPLY_MAX_FD_COUNT " + str(max_reply_fd_count) + "\n\n")
1614  
1615  public_header.write("""\
1616  #ifdef __cplusplus
1617  };
1618  #endif
1619  
1620  #endif // _DARLINGSERVER_API_H_
1621  """)
1622  
1623  public_header.close()
1624  internal_header.close()
1625  library_source.close()