scrcpy/app/src/util/process.h
Romain Vimont 143bfc0395 Expose inherit flag for process execution
Let the caller decide if stdout and stderr must be inherited on process
creation, i.e. if stdout and stderr of the child process should be
printed in the scrcpy console.

This allows to get output and errors for specific adb commands depending
on the context.
2021-11-20 00:21:46 +01:00

188 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_STDOUT (1 << 0)
#define SC_STDERR (1 << 1)
/**
* Execute the command and write the process id to `pid`
*
* The parameter `inherit` is a OR of any of SC_STDOUT and SC_STDERR. 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_STDOUT.
* If `perr` is not NULL, then `inherit` MUST NOT contain SC_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