summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qcore_unix.cpp
blob: 6861251bc2cc4008fbd5299b29885e2d1ae7b555 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include <QtCore/private/qglobal_p.h>
#include <QtCore/qbasicatomic.h>
#include "qcore_unix_p.h"

#include <stdlib.h>

#ifdef __GLIBC__
#  include <sys/syscall.h>
#  include <pthread.h>
#  include <unistd.h>
#endif

#ifdef Q_OS_DARWIN
#include <mach/mach_time.h>
#endif

QT_BEGIN_NAMESPACE

void qt_ignore_sigpipe() noexcept // noexcept: sigaction(2) is not a Posix Cancellation Point
{
    // Set to ignore SIGPIPE once only.
    Q_CONSTINIT static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
    if (!atom.loadRelaxed()) {
        // More than one thread could turn off SIGPIPE at the same time
        // But that's acceptable because they all would be doing the same
        // action
        struct sigaction noaction = {};
        noaction.sa_handler = SIG_IGN;
        ::sigaction(SIGPIPE, &noaction, nullptr);
        atom.storeRelaxed(1);
    }
}

QByteArray qt_readlink(const char *path)
{
#ifndef PATH_MAX
    // suitably large value that won't consume too much memory
#  define PATH_MAX  1024*1024
#endif

    QByteArray buf(256, Qt::Uninitialized);

    ssize_t len = ::readlink(path, buf.data(), buf.size());
    while (len == buf.size()) {
        // readlink(2) will fill our buffer and not necessarily terminate with NUL;
        if (buf.size() >= PATH_MAX) {
            errno = ENAMETOOLONG;
            return QByteArray();
        }

        // double the size and try again
        buf.resize(buf.size() * 2);
        len = ::readlink(path, buf.data(), buf.size());
    }

    if (len == -1)
        return QByteArray();

    buf.resize(len);
    return buf;
}

#if defined(Q_PROCESSOR_X86_32) && defined(__GLIBC__)
#  if !__GLIBC_PREREQ(2, 22)
// glibc prior to release 2.22 had a bug that suppresses the third argument to
// open() / open64() / openat(), causing file creation with O_TMPFILE to have
// the wrong permissions. So we bypass the glibc implementation and go straight
// for the syscall. See
// https://sourceware.org/git/?p=glibc.git;a=commit;h=65f6f938cd562a614a68e15d0581a34b177ec29d
int qt_open64(const char *pathname, int flags, mode_t mode)
{
    return syscall(SYS_open, pathname, flags | O_LARGEFILE, mode);
}
#  endif
#endif

#ifndef QT_BOOTSTRAPPED

#if QT_CONFIG(poll_pollts)
#  define ppoll pollts
#endif

[[maybe_unused]]
static inline int timespecToMillisecs(const struct timespec *ts)
{
    using namespace std::chrono;
    if (!ts)
        return -1;
    auto ms = ceil<milliseconds>(timespecToChrono<nanoseconds>(*ts));
    return int(ms.count());
}

// defined in qpoll.cpp
int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);

static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
{
#if QT_CONFIG(poll_ppoll) || QT_CONFIG(poll_pollts)
    return ::ppoll(fds, nfds, timeout_ts, nullptr);
#elif QT_CONFIG(poll_poll)
    return ::poll(fds, nfds, timespecToMillisecs(timeout_ts));
#elif QT_CONFIG(poll_select)
    return qt_poll(fds, nfds, timeout_ts);
#else
    // configure.json reports an error when everything is not available
#endif
}


/*!
    \internal

    Behaves as close to POSIX poll(2) as practical but may be implemented
    using select(2) where necessary. In that case, returns -1 and sets errno
    to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE.
*/
int qt_safe_poll(struct pollfd *fds, nfds_t nfds, QDeadlineTimer deadline)
{
    if (deadline.isForever()) {
        // no timeout -> block forever
        int ret;
        QT_EINTR_LOOP(ret, qt_ppoll(fds, nfds, nullptr));
        return ret;
    }

    using namespace std::chrono;
    nanoseconds remaining = deadline.remainingTimeAsDuration();
    // loop and recalculate the timeout as needed
    do {
        timespec ts = durationToTimespec(remaining);
        const int ret = qt_ppoll(fds, nfds, &ts);
        if (ret != -1 || errno != EINTR)
            return ret;
        remaining = deadline.remainingTimeAsDuration();
    } while (remaining > 0ns);

    return 0;
}

#endif // QT_BOOTSTRAPPED

QT_END_NAMESPACE