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