scrcpy/app/src/util/process.h
2021-11-19 22:27:01 +01:00

189 lines
4.6 KiB
C

#ifndef SC_PROCESS_H
#define SC_PROCESS_H
#include "common.h"
#include <stdbool.h>
#include "util/thread.h"
#ifdef _WIN32
// not needed here, but winsock2.h must never be included AFTER windows.h
# include <winsock2.h>
# include <windows.h>
# define SC_PRIexitcode "lu"
// <https://stackoverflow.com/a/44383330/1987178>
# define SC_PRIsizet "Iu"
# define SC_PROCESS_NONE NULL
# define SC_EXIT_CODE_NONE -1u // max value as unsigned
typedef HANDLE sc_pid;
typedef DWORD sc_exit_code;
typedef HANDLE sc_pipe;
#else
# include <sys/types.h>
# define SC_PRIsizet "zu"
# define SC_PRIexitcode "d"
# define SC_PROCESS_NONE -1
# define SC_EXIT_CODE_NONE -1
typedef pid_t sc_pid;
typedef int sc_exit_code;
typedef int sc_pipe;
#endif
struct sc_process_listener {
void (*on_terminated)(void *userdata);
};
/**
* Tool to observe process termination
*
* To keep things simple and multiplatform, it runs a separate thread to wait
* for process termination (without closing the process to avoid race
* conditions).
*
* It allows a caller to block until the process is terminated (with a
* timeout), and to be notified asynchronously from the observer thread.
*
* The process is not owned by the observer (the observer will never close it).
*/
struct sc_process_observer {
sc_pid pid;
sc_mutex mutex;
sc_cond cond_terminated;
bool terminated;
sc_thread thread;
const struct sc_process_listener *listener;
void *listener_userdata;
};
enum sc_process_result {
SC_PROCESS_SUCCESS,
SC_PROCESS_ERROR_GENERIC,
SC_PROCESS_ERROR_MISSING_BINARY,
};
#define SC_INHERIT_NONE 0
#define SC_INHERIT_STDOUT (1 << 0)
#define SC_INHERIT_STDERR (1 << 1)
/**
* Execute the command and write the process id to `pid`
*
* The parameter `inherit` is a ORed value of SC_INHERIT_* flags. It indicates
* if stdout and stderr must be inherited from the scrcpy process (in other
* words, if the process must output to the scrcpy console).
*/
enum sc_process_result
sc_process_execute(const char *const argv[], sc_pid *pid, unsigned inherit);
/**
* Execute the command and write the process id to `pid`
*
* If not NULL, provide a pipe for stdin (`pin`), stdout (`pout`) and stderr
* (`perr`).
*
* The parameter `inherit` has the same semantics as in `sc_process_execute()`.
* If `pout` is not NULL, then `inherit` MUST NOT contain SC_INHERIT_STDOUT.
* If `perr` is not NULL, then `inherit` MUST NOT contain SC_INHERIT_STDERR.
*/
enum sc_process_result
sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit,
sc_pipe *pin, sc_pipe *pout, sc_pipe *perr);
/**
* Kill the process
*/
bool
sc_process_terminate(sc_pid pid);
/**
* Wait and close the process (similar to waitpid())
*
* The `close` flag indicates if the process must be _closed_ (reaped) (passing
* false is equivalent to enable WNOWAIT in waitid()).
*/
sc_exit_code
sc_process_wait(sc_pid pid, bool close);
/**
* Close (reap) the process
*
* Semantically:
* sc_process_wait(close) = sc_process_wait(noclose) + sc_process_close()
*/
void
sc_process_close(sc_pid pid);
/**
* Convenience function to wait for a successful process execution
*
* Automatically log process errors with the provided process name.
*/
bool
sc_process_check_success(sc_pid pid, const char *name, bool close);
/**
* Read from the pipe
*
* Same semantic as read().
*/
ssize_t
sc_pipe_read(sc_pipe pipe, char *data, size_t len);
/**
* Read exactly `len` chars from a pipe (unless EOF)
*/
ssize_t
sc_pipe_read_all(sc_pipe pipe, char *data, size_t len);
/**
* Close the pipe
*/
void
sc_pipe_close(sc_pipe pipe);
/**
* Start observing process
*
* The listener is optional. If set, its callback will be called from the
* observer thread once the process is terminated.
*/
bool
sc_process_observer_init(struct sc_process_observer *observer, sc_pid pid,
const struct sc_process_listener *listener,
void *listener_userdata);
/**
* Wait for process termination until a deadline
*
* Return true if the process is already terminated. Return false if the
* process terminatation has not been detected yet (however, it may have
* terminated in the meantime).
*
* To wait without timeout/deadline, just use sc_process_wait() instead.
*/
bool
sc_process_observer_timedwait(struct sc_process_observer *observer,
sc_tick deadline);
/**
* Join the observer thread
*/
void
sc_process_observer_join(struct sc_process_observer *observer);
/**
* Destroy the observer
*
* This does not close the associated process.
*/
void
sc_process_observer_destroy(struct sc_process_observer *observer);
#endif