1 /* Copyright 2014-present Facebook, Inc.
2 * Licensed under the Apache License, Version 2.0 */
3
4 #include "watchman.h"
5 #ifdef HAVE_SYS_UCRED_H
6 #include <sys/ucred.h>
7 #endif
8 #ifdef HAVE_SYS_SOCKET_H
9 #include <sys/socket.h>
10 #endif
11 #include "FileDescriptor.h"
12 #include "Pipe.h"
13 #include "make_unique.h"
14
15 using watchman::FileDescriptor;
16 using watchman::Pipe;
17
18 static const int kWriteTimeout = 60000;
19
20 namespace {
21 // This trait allows w_poll_events to wait on either a PipeEvent or
22 // a descriptor contained in a UnixStream
23 class PollableEvent : public watchman_event {
24 public:
25 virtual int getFd() const = 0;
26 };
27
28 // The event object, implemented as pipe
29 class PipeEvent : public PollableEvent {
30 public:
31 Pipe pipe;
32
notify()33 void notify() override {
34 ignore_result(write(pipe.write.fd(), "a", 1));
35 }
36
testAndClear()37 bool testAndClear() override {
38 char buf[64];
39 bool signalled = false;
40 while (read(pipe.read.fd(), buf, sizeof(buf)) > 0) {
41 signalled = true;
42 }
43 return signalled;
44 }
45
getFd() const46 int getFd() const override {
47 return pipe.read.fd();
48 }
49 };
50
51 // Event object that UnixStream returns via getEvents.
52 // It cannot be poked by hand; it is just a helper to
53 // allow waiting on a socket using w_poll_events.
54 class FakeSocketEvent : public PollableEvent {
55 public:
56 int socket;
57
FakeSocketEvent(int fd)58 explicit FakeSocketEvent(int fd) : socket(fd) {}
59
notify()60 void notify() override {}
testAndClear()61 bool testAndClear() override {
62 return false;
63 }
getFd() const64 int getFd() const override {
65 return socket;
66 }
67 };
68
69 class UnixStream : public watchman_stream {
70 public:
71 FileDescriptor fd;
72 FakeSocketEvent evt;
73 #ifdef SO_PEERCRED
74 struct ucred cred;
75 #elif defined(LOCAL_PEERCRED)
76 struct xucred cred;
77 #endif
78 bool credvalid{false};
79
UnixStream(FileDescriptor && descriptor)80 explicit UnixStream(FileDescriptor&& descriptor)
81 : fd(std::move(descriptor)), evt(fd.fd()) {
82 socklen_t len = sizeof(cred);
83 #ifdef SO_PEERCRED
84 credvalid = getsockopt(fd.fd(), SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0;
85 #elif defined(LOCAL_PEERCRED)
86 credvalid =
87 getsockopt(fd.fd(), SOL_LOCAL, LOCAL_PEERCRED, &cred, &len) == 0;
88 #endif
89 }
90
getFileDescriptor() const91 const FileDescriptor& getFileDescriptor() const override {
92 return fd;
93 }
94
read(void * buf,int size)95 int read(void* buf, int size) override {
96 errno = 0;
97 return ::read(fd.fd(), buf, size);
98 }
99
write(const void * buf,int size)100 int write(const void* buf, int size) override {
101 errno = 0;
102 if (!fd.isNonBlock()) {
103 int wrote = 0;
104
105 while (size > 0) {
106 struct pollfd pfd;
107 pfd.fd = fd.fd();
108 pfd.events = POLLOUT;
109 if (poll(&pfd, 1, kWriteTimeout) == 0) {
110 break;
111 }
112 if (pfd.revents & (POLLERR | POLLHUP)) {
113 break;
114 }
115 auto x = ::write(fd.fd(), buf, size);
116 if (x <= 0) {
117 break;
118 }
119
120 wrote += x;
121 size -= x;
122 buf = reinterpret_cast<const void*>(
123 reinterpret_cast<const char*>(buf) + x);
124 }
125 return wrote == 0 ? -1 : wrote;
126 }
127 return ::write(fd.fd(), buf, size);
128 }
129
getEvents()130 w_evt_t getEvents() override {
131 return &evt;
132 }
133
setNonBlock(bool nonb)134 void setNonBlock(bool nonb) override {
135 if (nonb) {
136 fd.setNonBlock();
137 } else {
138 fd.clearNonBlock();
139 }
140 }
141
rewind()142 bool rewind() override {
143 return lseek(fd.fd(), 0, SEEK_SET) == 0;
144 }
145
shutdown()146 bool shutdown() override {
147 return ::shutdown(fd.fd(), SHUT_RDWR);
148 }
149
150 // For these PEERCRED things, the uid reported is the effective uid of
151 // the process, which may have been altered due to setuid or similar
152 // mechanisms. We'll treat the other process as an owner if their
153 // effective UID matches ours, or if they are root.
peerIsOwner()154 bool peerIsOwner() override {
155 if (!credvalid) {
156 return false;
157 }
158 #ifdef SO_PEERCRED
159 if (cred.uid == getuid() || cred.uid == 0) {
160 return true;
161 }
162 #elif defined(LOCAL_PEERCRED)
163 if (cred.cr_uid == getuid() || cred.cr_uid == 0) {
164 return true;
165 }
166 #endif
167 return false;
168 }
169
getPeerProcessID() const170 pid_t getPeerProcessID() const override {
171 if (!credvalid) {
172 return 0;
173 }
174 #ifdef SO_PEERCRED
175 return cred.pid;
176 #else
177 return 0;
178 #endif
179 }
180 };
181 }
182
w_event_make(void)183 std::unique_ptr<watchman_event> w_event_make(void) {
184 return watchman::make_unique<PipeEvent>();
185 }
186
187 #define MAX_POLL_EVENTS 63 // Must match MAXIMUM_WAIT_OBJECTS-1 on win
w_poll_events(struct watchman_event_poll * p,int n,int timeoutms)188 int w_poll_events(struct watchman_event_poll *p, int n, int timeoutms) {
189 struct pollfd pfds[MAX_POLL_EVENTS];
190 int i;
191 int res;
192
193 if (n > MAX_POLL_EVENTS) {
194 // Programmer error :-/
195 w_log(W_LOG_FATAL, "%d > MAX_POLL_EVENTS (%d)\n", n, MAX_POLL_EVENTS);
196 }
197
198 for (i = 0; i < n; i++) {
199 auto pe = dynamic_cast<PollableEvent*>(p[i].evt);
200 w_check(pe != nullptr, "PollableEvent!?");
201 pfds[i].fd = pe->getFd();
202 pfds[i].events = POLLIN|POLLHUP|POLLERR;
203 pfds[i].revents = 0;
204 }
205
206 res = poll(pfds, n, timeoutms);
207
208 for (i = 0; i < n; i++) {
209 p[i].ready = pfds[i].revents != 0;
210 }
211
212 return res;
213 }
214
w_stm_fdopen(FileDescriptor && fd)215 std::unique_ptr<watchman_stream> w_stm_fdopen(FileDescriptor&& fd) {
216 if (!fd) {
217 return nullptr;
218 }
219 return watchman::make_unique<UnixStream>(std::move(fd));
220 }
221
w_stm_connect_unix(const char * path,int timeoutms)222 std::unique_ptr<watchman_stream> w_stm_connect_unix(
223 const char* path,
224 int timeoutms) {
225 struct sockaddr_un un;
226 int max_attempts = timeoutms / 10;
227 int attempts = 0;
228 int bufsize = WATCHMAN_IO_BUF_SIZE;
229
230 if (strlen(path) >= sizeof(un.sun_path) - 1) {
231 w_log(W_LOG_ERR, "w_stm_connect_unix(%s) path is too long\n", path);
232 errno = E2BIG;
233 return NULL;
234 }
235
236 FileDescriptor fd(socket(PF_LOCAL, SOCK_STREAM, 0));
237 if (!fd) {
238 return nullptr;
239 }
240
241 memset(&un, 0, sizeof(un));
242 un.sun_family = PF_LOCAL;
243 memcpy(un.sun_path, path, strlen(path));
244
245 retry_connect:
246
247 if (connect(fd.fd(), (struct sockaddr*)&un, sizeof(un))) {
248 int err = errno;
249
250 if (err == ECONNREFUSED || err == ENOENT) {
251 if (attempts++ < max_attempts) {
252 usleep(10000);
253 goto retry_connect;
254 }
255 }
256
257 return nullptr;
258 }
259
260 setsockopt(fd.fd(), SOL_SOCKET, SO_RCVBUF, (void*)&bufsize, sizeof(bufsize));
261
262 return w_stm_fdopen(std::move(fd));
263 }
264
265 std::unique_ptr<watchman_stream>
w_stm_open(const char * filename,int flags,...)266 w_stm_open(const char* filename, int flags, ...) {
267 int mode = 0;
268
269 // If we're creating, pull out the mode flag
270 if (flags & O_CREAT) {
271 va_list ap;
272 va_start(ap, flags);
273 mode = va_arg(ap, int);
274 va_end(ap);
275 }
276
277 return w_stm_fdopen(FileDescriptor(open(filename, flags, mode)));
278 }
279