Skip to content

Commit 79019d2

Browse files
committed
[android] Implement Process and modify TestProcess to pass.
Until recent API levels, Android haven't provide posix_spawn and other functions from that family, so Process was not going to work with the branches used for other POSIX OS. Instead of creating an specific path for Android, which will probably be forgotten by other changes, all the POSIX OS will use private CF functions that wrap the posix_spawn functions. For Android, we try to figure out if posix_spawn exist, and use it in that case, but we implement a subset of the posix_spawn family of functions for lower API levels, using fork and exec underneath, and other functions from the POSIX APIs. The already code in Process is only modified slightly to adapt to the new names have to be modified. The tests have to be modified slightly for working on Android. Many changes are related to the environment needing the key LD_LIBRARY_PATH, or the xdgTestHelper will not be able to start in some of the tests. The other change is using NSTemporaryDirectory() instead of a hardcoded `/tmp`, which doesn't exist in Android (the code also removes the last slash, to match the original code expectations). It also includes a small fix for when launch() is used and a error is thrown which is not related to the current working directory. Before the error was ignored silently, and now it will fatalError. Other small improvement is reporting the actual line in the helper for the returned POSIX error codes, instead of always reporting the line of the helper itself.
1 parent 7c9fb6a commit 79019d2

File tree

4 files changed

+445
-36
lines changed

4 files changed

+445
-36
lines changed

CoreFoundation/Base.subproj/CFPlatform.c

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,4 +1546,374 @@ CF_CROSS_PLATFORM_EXPORT void *_CFReallocf(void *ptr, size_t size) {
15461546
#endif
15471547
}
15481548

1549+
#if TARGET_OS_ANDROID
1550+
1551+
#include <dlfcn.h>
1552+
#include <spawn.h>
1553+
1554+
// Android doesn't provide posix_spawn APIs until recent API level, so we cannot
1555+
// depend on them, but we can imitate the API, and perform the same work.
1556+
1557+
static pthread_once_t posixSpawnOnce = PTHREAD_ONCE_INIT;
1558+
static _CFPosixSpawnFileActionsRef (*_CFPosixSpawnFileActionsAllocImpl)(void);
1559+
static int (*_CFPosixSpawnFileActionsInitImpl)(_CFPosixSpawnFileActionsRef);
1560+
static int (*_CFPosixSpawnFileActionsDestroyImpl)(_CFPosixSpawnFileActionsRef);
1561+
static void (*_CFPosixSpawnFileActionsDeallocImpl)(_CFPosixSpawnFileActionsRef);
1562+
static int (*_CFPosixSpawnFileActionsAddDup2Impl)(_CFPosixSpawnFileActionsRef, int, int);
1563+
static int (*_CFPosixSpawnFileActionsAddCloseImpl)(_CFPosixSpawnFileActionsRef, int);
1564+
static int (*_CFPosixSpawnImpl)(pid_t *_CF_RESTRICT, const char *_CF_RESTRICT, _CFPosixSpawnFileActionsRef, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT, char *_Nullable const[_Nullable _CF_RESTRICT], char *_Nullable const[_Nullable _CF_RESTRICT]);
1565+
1566+
static _CFPosixSpawnFileActionsRef _CFPosixSpawnFileActionsAllocImplPost28() {
1567+
_CFPosixSpawnFileActionsRef actions = malloc(sizeof(posix_spawn_file_actions_t));
1568+
CFAssert(actions != NULL, __kCFLogAssertion, "malloc failed");
1569+
return actions;
1570+
}
1571+
1572+
static void _CFPosixSpawnFileActionsDeallocImplBoth(_CFPosixSpawnFileActionsRef file_actions) {
1573+
free(file_actions);
1574+
}
1575+
1576+
enum _CFPosixSpawnFileActionTypePre28 {
1577+
_CFPosixSpawnFileActionDup2Pre28,
1578+
_CFPosixSpawnFileActionClosePre28,
1579+
};
1580+
1581+
struct _CFPosixSpawnFileActionPre28 {
1582+
enum _CFPosixSpawnFileActionTypePre28 type;
1583+
union {
1584+
struct {
1585+
int filedes;
1586+
int newfiledes;
1587+
} dup2Action;
1588+
struct {
1589+
int filedes;
1590+
} closeAction;
1591+
};
1592+
};
1593+
1594+
struct _CFPosixSpawnFileActionsPre28 {
1595+
struct _CFPosixSpawnFileActionPre28 *actions;
1596+
size_t actionsCount;
1597+
size_t actionsCapacity;
1598+
int32_t isValid;
1599+
};
1600+
1601+
static const int32_t _CFPosixSpawnFileActionsPre28Valid = 0x600D600D;
1602+
1603+
static _CFPosixSpawnFileActionsRef _CFPosixSpawnFileActionsAllocImplPre28() {
1604+
_CFPosixSpawnFileActionsRef actions = calloc(1, sizeof(struct _CFPosixSpawnFileActionsPre28));
1605+
CFAssert(actions != NULL, __kCFLogAssertion, "malloc failed");
1606+
return actions;
1607+
}
1608+
1609+
static int _CFPosixSpawnFileActionsInitImplPre28(_CFPosixSpawnFileActionsRef file_actions) {
1610+
if (file_actions == NULL) {
1611+
return EINVAL;
1612+
}
1613+
1614+
struct _CFPosixSpawnFileActionsPre28 *actions = (struct _CFPosixSpawnFileActionsPre28 *)file_actions;
1615+
1616+
actions->actions = malloc(8 * sizeof(struct _CFPosixSpawnFileActionPre28));
1617+
if (actions->actions == NULL) {
1618+
return ENOMEM;
1619+
}
1620+
actions->actionsCount = 0;
1621+
actions->actionsCapacity = 8;
1622+
1623+
actions->isValid = _CFPosixSpawnFileActionsPre28Valid;
1624+
1625+
return 0;
1626+
}
1627+
1628+
static int _CFPosixSpawnFileActionsDestroyImplPre28(_CFPosixSpawnFileActionsRef file_actions) {
1629+
if (file_actions == NULL) {
1630+
return EINVAL;
1631+
}
1632+
1633+
struct _CFPosixSpawnFileActionsPre28 *actions = (struct _CFPosixSpawnFileActionsPre28 *)file_actions;
1634+
if (actions->isValid != _CFPosixSpawnFileActionsPre28Valid) {
1635+
return EINVAL;
1636+
}
1637+
1638+
free(actions->actions);
1639+
actions->actionsCount = 0;
1640+
actions->actionsCapacity = 0;
1641+
1642+
actions->isValid = 0;
1643+
1644+
return 0;
1645+
}
1646+
1647+
static int _CFPosixSpawnFileActionsAddDup2ImplPre28(_CFPosixSpawnFileActionsRef file_actions, int filedes, int newfiledes) {
1648+
if (file_actions == NULL) {
1649+
return EINVAL;
1650+
}
1651+
1652+
if (filedes < 0 || newfiledes < 0) {
1653+
return EBADF;
1654+
}
1655+
1656+
struct _CFPosixSpawnFileActionsPre28 *actions = (struct _CFPosixSpawnFileActionsPre28 *)file_actions;
1657+
if (actions->isValid != _CFPosixSpawnFileActionsPre28Valid) {
1658+
return EINVAL;
1659+
}
1660+
1661+
if (actions->actionsCount == actions->actionsCapacity) {
1662+
struct _CFPosixSpawnFileActionPre28 *newActions = realloc(actions->actions, actions->actionsCapacity * 2);
1663+
if (newActions == NULL) {
1664+
return ENOMEM;
1665+
}
1666+
actions->actions = newActions;
1667+
actions->actionsCapacity *= 2;
1668+
}
1669+
1670+
actions->actions[actions->actionsCount++] = (struct _CFPosixSpawnFileActionPre28) {
1671+
.type = _CFPosixSpawnFileActionDup2Pre28,
1672+
.dup2Action = {
1673+
.filedes = filedes,
1674+
.newfiledes = newfiledes
1675+
}
1676+
};
1677+
1678+
return 0;
1679+
}
1680+
1681+
static int _CFPosixSpawnFileActionsAddCloseImplPre28(_CFPosixSpawnFileActionsRef file_actions, int filedes) {
1682+
if (file_actions == NULL) {
1683+
return EINVAL;
1684+
}
1685+
1686+
if (filedes < 0) {
1687+
return EBADF;
1688+
}
1689+
1690+
struct _CFPosixSpawnFileActionsPre28 *actions = (struct _CFPosixSpawnFileActionsPre28 *)file_actions;
1691+
if (actions->isValid != _CFPosixSpawnFileActionsPre28Valid) {
1692+
return EINVAL;
1693+
}
1694+
1695+
if (actions->actionsCount == actions->actionsCapacity) {
1696+
struct _CFPosixSpawnFileActionPre28 *newActions = realloc(actions->actions, actions->actionsCapacity * 2);
1697+
if (newActions == NULL) {
1698+
return ENOMEM;
1699+
}
1700+
actions->actions = newActions;
1701+
actions->actionsCapacity *= 2;
1702+
}
1703+
1704+
actions->actions[actions->actionsCount++] = (struct _CFPosixSpawnFileActionPre28) {
1705+
.type = _CFPosixSpawnFileActionClosePre28,
1706+
.closeAction = {
1707+
.filedes = filedes
1708+
}
1709+
};
1710+
1711+
return 0;
1712+
}
1713+
1714+
static int _CFPosixSpawnImplPre28(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *_Nullable const argv[_Nullable _CF_RESTRICT], char *_Nullable const envp[_Nullable _CF_RESTRICT]) {
1715+
// TODO: We completely ignore attrp, because at the moment, the only
1716+
// invocation doesn't pass a value.
1717+
if (attrp != NULL) {
1718+
return EINVAL;
1719+
}
1720+
1721+
struct _CFPosixSpawnFileActionsPre28 *actions = (struct _CFPosixSpawnFileActionsPre28 *)file_actions;
1722+
if (actions != NULL && actions->isValid != _CFPosixSpawnFileActionsPre28Valid) {
1723+
return EINVAL;
1724+
}
1725+
1726+
// Block signals during this fork/execv dance.
1727+
sigset_t signalSet;
1728+
sigfillset(&signalSet);
1729+
sigset_t oldMask;
1730+
if (sigprocmask(SIG_BLOCK, &signalSet, &oldMask) != 0) {
1731+
CFAssert1(FALSE, __kCFLogAssertion, "sigprocmask() failed: %d", errno);
1732+
}
1733+
1734+
pid_t forkPid = fork();
1735+
if (forkPid != 0) {
1736+
// This is the parent. fork call might have been successful or not.
1737+
1738+
// Unblock signals.
1739+
if (sigprocmask(SIG_SETMASK, &oldMask, NULL) != 0) {
1740+
CFAssert1(FALSE, __kCFLogAssertion, "sigprocmask() failed: %d", errno);
1741+
}
1742+
1743+
if (forkPid < 0) {
1744+
return forkPid;
1745+
}
1746+
1747+
if (pid != NULL) {
1748+
*pid = forkPid;
1749+
}
1750+
1751+
return 0;
1752+
}
1753+
1754+
// This is the child.
1755+
1756+
// Clean up the parent signal handlers
1757+
for (int idx = 1; idx < NSIG; idx++) {
1758+
// Seems that SIGKILL/SIGSTOP are sometimes silently ignored, and
1759+
// sometimes return EINVAL. Since one cannot change the handlers anyway,
1760+
// skip them.
1761+
if (idx == SIGKILL || idx == SIGSTOP) {
1762+
continue;
1763+
}
1764+
1765+
struct sigaction sigAction;
1766+
if (sigaction(idx, NULL, &sigAction) != 0) {
1767+
exit(127);
1768+
}
1769+
1770+
if (sigAction.sa_handler != SIG_IGN) {
1771+
sigAction.sa_handler = SIG_DFL;
1772+
if (sigaction(idx, &sigAction, NULL) != 0) {
1773+
exit(127);
1774+
}
1775+
}
1776+
}
1777+
1778+
// Perform the actions
1779+
if (actions != NULL) {
1780+
for (size_t idx = 0; idx < actions->actionsCount; idx++) {
1781+
struct _CFPosixSpawnFileActionPre28 *action = &(actions->actions[idx]);
1782+
if (action->type == _CFPosixSpawnFileActionDup2Pre28) {
1783+
if (dup2(action->dup2Action.filedes, action->dup2Action.newfiledes) < 0) {
1784+
exit(127);
1785+
}
1786+
} else if (actions->actions[idx].type == _CFPosixSpawnFileActionClosePre28) {
1787+
if (close(action->closeAction.filedes) != 0) {
1788+
exit(127);
1789+
}
1790+
}
1791+
}
1792+
}
1793+
1794+
// Unblock the signals
1795+
if (sigprocmask(SIG_SETMASK, &oldMask, NULL) != 0) {
1796+
CFAssert1(FALSE, __kCFLogAssertion, "sigprocmask() failed: %d", errno);
1797+
}
1798+
1799+
// If execv fails, we will simply exit 127 as the standard says.
1800+
execve(path, argv, envp ?: environ);
1801+
exit(127);
1802+
// no need for return here
1803+
}
1804+
1805+
static void _CFPosixSpawnInitializeCallback() {
1806+
// Let's check if the posix_spawn is present.
1807+
(void)dlerror(); // Clean up the error.
1808+
_CFPosixSpawnImpl = (int (*)(pid_t *_CF_RESTRICT, const char *_CF_RESTRICT, void *, void *_CF_RESTRICT, char *const *_CF_RESTRICT, char *const *_CF_RESTRICT))dlsym(RTLD_DEFAULT, "posix_spawn");
1809+
char *dlsymError = dlerror();
1810+
CFAssert1(dlsymError == NULL, __kCFLogAssertion, "dlsym failed: %s", dlsymError);
1811+
if (_CFPosixSpawnImpl != NULL) {
1812+
// posix_spawn_fn is available, so use it
1813+
_CFPosixSpawnFileActionsAllocImpl = _CFPosixSpawnFileActionsAllocImplPost28;
1814+
_CFPosixSpawnFileActionsDeallocImpl = _CFPosixSpawnFileActionsDeallocImplBoth;
1815+
1816+
_CFPosixSpawnFileActionsInitImpl = (int (*)(void *))dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_init");
1817+
dlsymError = dlerror();
1818+
CFAssert1(_CFPosixSpawnFileActionsInitImpl != NULL, __kCFLogAssertion, "loading posix_spawn_file_actions_init failed: %s", dlsymError);
1819+
1820+
_CFPosixSpawnFileActionsDestroyImpl = (int (*)(void *))dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_destroy");
1821+
dlsymError = dlerror();
1822+
CFAssert1(_CFPosixSpawnFileActionsDestroyImpl != NULL, __kCFLogAssertion, "loading posix_spawn_file_actions_destroy failed: %s", dlsymError);
1823+
1824+
_CFPosixSpawnFileActionsAddDup2Impl = (int (*)(void *, int, int))dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_adddup2");
1825+
dlsymError = dlerror();
1826+
CFAssert1(_CFPosixSpawnFileActionsAddDup2Impl != NULL, __kCFLogAssertion, "loading posix_spawn_file_actions_adddup2 failed: %s", dlsymError);
1827+
1828+
_CFPosixSpawnFileActionsAddCloseImpl = (int (*)(void *, int))dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addclose");
1829+
dlsymError = dlerror();
1830+
CFAssert1(_CFPosixSpawnFileActionsAddCloseImpl != NULL, __kCFLogAssertion, "loading posix_spawn_file_actions_addclose failed: %s", dlsymError);
1831+
} else {
1832+
// posix_spawn_fn is not available, setup our workaround
1833+
_CFPosixSpawnFileActionsAllocImpl = _CFPosixSpawnFileActionsAllocImplPre28;
1834+
_CFPosixSpawnFileActionsDeallocImpl = _CFPosixSpawnFileActionsDeallocImplBoth;
1835+
_CFPosixSpawnFileActionsInitImpl = _CFPosixSpawnFileActionsInitImplPre28;
1836+
_CFPosixSpawnFileActionsDestroyImpl = _CFPosixSpawnFileActionsDestroyImplPre28;
1837+
_CFPosixSpawnFileActionsAddDup2Impl = _CFPosixSpawnFileActionsAddDup2ImplPre28;
1838+
_CFPosixSpawnFileActionsAddCloseImpl = _CFPosixSpawnFileActionsAddCloseImplPre28;
1839+
_CFPosixSpawnImpl = _CFPosixSpawnImplPre28;
1840+
}
1841+
}
1842+
1843+
static void _CFPosixSpawnInitialize() {
1844+
int r = pthread_once(&posixSpawnOnce, _CFPosixSpawnInitializeCallback);
1845+
CFAssert(r == 0, __kCFLogAssertion, "pthread_once failed");
1846+
}
1847+
1848+
CF_EXPORT _CFPosixSpawnFileActionsRef _CFPosixSpawnFileActionsAlloc() {
1849+
_CFPosixSpawnInitialize();
1850+
return _CFPosixSpawnFileActionsAllocImpl();
1851+
}
1852+
1853+
CF_EXPORT int _CFPosixSpawnFileActionsInit(_CFPosixSpawnFileActionsRef file_actions) {
1854+
_CFPosixSpawnInitialize();
1855+
return _CFPosixSpawnFileActionsInitImpl(file_actions);
1856+
}
1857+
1858+
CF_EXPORT int _CFPosixSpawnFileActionsDestroy(_CFPosixSpawnFileActionsRef file_actions) {
1859+
_CFPosixSpawnInitialize();
1860+
return _CFPosixSpawnFileActionsDestroyImpl(file_actions);
1861+
}
1862+
1863+
CF_EXPORT void _CFPosixSpawnFileActionsDealloc(_CFPosixSpawnFileActionsRef file_actions) {
1864+
_CFPosixSpawnInitialize();
1865+
_CFPosixSpawnFileActionsDeallocImpl(file_actions);
1866+
}
1867+
1868+
CF_EXPORT int _CFPosixSpawnFileActionsAddDup2(_CFPosixSpawnFileActionsRef file_actions, int filedes, int newfiledes) {
1869+
_CFPosixSpawnInitialize();
1870+
return _CFPosixSpawnFileActionsAddDup2Impl(file_actions, filedes, newfiledes);
1871+
}
1872+
1873+
CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_actions, int filedes) {
1874+
_CFPosixSpawnInitialize();
1875+
return _CFPosixSpawnFileActionsAddCloseImpl(file_actions, filedes);
1876+
}
1877+
1878+
CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *_Nullable const argv[_Nullable _CF_RESTRICT], char *_Nullable const envp[_Nullable _CF_RESTRICT]) {
1879+
_CFPosixSpawnInitialize();
1880+
return _CFPosixSpawnImpl(pid, path, file_actions, attrp, argv, envp);
1881+
}
1882+
1883+
#elif !TARGET_OS_WIN32
1884+
1885+
#include <spawn.h>
1886+
1887+
CF_EXPORT _CFPosixSpawnFileActionsRef _CFPosixSpawnFileActionsAlloc() {
1888+
_CFPosixSpawnFileActionsRef actions = malloc(sizeof(posix_spawn_file_actions_t));
1889+
CFAssert(actions != NULL, __kCFLogAssertion, "malloc failed");
1890+
return actions;
1891+
}
1892+
1893+
CF_EXPORT int _CFPosixSpawnFileActionsInit(_CFPosixSpawnFileActionsRef file_actions) {
1894+
return posix_spawn_file_actions_init((posix_spawn_file_actions_t *)file_actions);
1895+
}
1896+
1897+
CF_EXPORT int _CFPosixSpawnFileActionsDestroy(_CFPosixSpawnFileActionsRef file_actions) {
1898+
return posix_spawn_file_actions_destroy((posix_spawn_file_actions_t *)file_actions);
1899+
}
1900+
1901+
CF_EXPORT void _CFPosixSpawnFileActionsDealloc(_CFPosixSpawnFileActionsRef file_actions) {
1902+
free(file_actions);
1903+
}
1904+
1905+
CF_EXPORT int _CFPosixSpawnFileActionsAddDup2(_CFPosixSpawnFileActionsRef file_actions, int filedes, int newfiledes) {
1906+
return posix_spawn_file_actions_adddup2((posix_spawn_file_actions_t *)file_actions, filedes, newfiledes);
1907+
}
1908+
1909+
CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_actions, int filedes) {
1910+
return posix_spawn_file_actions_addclose((posix_spawn_file_actions_t *)file_actions, filedes);
1911+
}
1912+
1913+
CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *_Nullable const argv[_Nullable _CF_RESTRICT], char *_Nullable const envp[_Nullable _CF_RESTRICT]) {
1914+
return posix_spawn(pid, path, (posix_spawn_file_actions_t *)file_actions, (posix_spawnattr_t *)attrp, argv, envp);
1915+
}
1916+
1917+
#endif
1918+
15491919
#endif

0 commit comments

Comments
 (0)