1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "tscore/ink_platform.h"
25 #include "tscore/ink_assert.h"
26 #include "tscore/ink_cap.h"
27 #include "MgmtSocket.h"
28
29 #if HAVE_UCRED_H
30 #include <ucred.h>
31 #endif
32
33 //-------------------------------------------------------------------------
34 // defines
35 //-------------------------------------------------------------------------
36
37 #define MGMT_MAX_TRANSIENT_ERRORS 64
38
39 //-------------------------------------------------------------------------
40 // transient_error
41 //-------------------------------------------------------------------------
42
43 bool
mgmt_transient_error()44 mgmt_transient_error()
45 {
46 switch (errno) {
47 case EINTR:
48 case EAGAIN:
49
50 #ifdef ENOMEM
51 case ENOMEM:
52 #endif
53
54 #ifdef ENOBUF
55 case ENOBUF:
56 #endif
57
58 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
59 case EWOULDBLOCK:
60 #endif
61
62 return true;
63
64 default:
65 return false;
66 }
67 }
68
69 //-------------------------------------------------------------------------
70 // system calls (based on implementation from UnixSocketManager)
71 //-------------------------------------------------------------------------
72
73 //-------------------------------------------------------------------------
74 // mgmt_accept
75 //-------------------------------------------------------------------------
76
77 int
mgmt_accept(int s,struct sockaddr * addr,socklen_t * addrlen)78 mgmt_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
79 {
80 int r, retries;
81 ink_assert(*addrlen != 0);
82 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
83 r = ::accept(s, addr, addrlen);
84 if (r >= 0) {
85 return r;
86 }
87 if (!mgmt_transient_error()) {
88 break;
89 }
90 }
91 return r;
92 }
93
94 //-------------------------------------------------------------------------
95 // mgmt_fopen
96 //-------------------------------------------------------------------------
97
98 FILE *
mgmt_fopen(const char * filename,const char * mode)99 mgmt_fopen(const char *filename, const char *mode)
100 {
101 FILE *f;
102 int retries;
103 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
104 // no leak here as f will be returned if it is > 0
105 // coverity[overwrite_var]
106 f = ::fopen(filename, mode);
107 if (f != nullptr) {
108 return f;
109 }
110 if (!mgmt_transient_error()) {
111 break;
112 }
113 }
114 return f;
115 }
116
117 //-------------------------------------------------------------------------
118 // mgmt_open
119 //-------------------------------------------------------------------------
120
121 int
mgmt_open(const char * path,int oflag)122 mgmt_open(const char *path, int oflag)
123 {
124 int r, retries;
125 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
126 r = ::open(path, oflag);
127 if (r >= 0) {
128 return r;
129 }
130 if (!mgmt_transient_error()) {
131 break;
132 }
133 }
134 return r;
135 }
136
137 //-------------------------------------------------------------------------
138 // mgmt_open_mode
139 //-------------------------------------------------------------------------
140
141 int
mgmt_open_mode(const char * path,int oflag,mode_t mode)142 mgmt_open_mode(const char *path, int oflag, mode_t mode)
143 {
144 int r, retries;
145 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
146 r = ::open(path, oflag, mode);
147 if (r >= 0) {
148 return r;
149 }
150 if (!mgmt_transient_error()) {
151 break;
152 }
153 }
154 return r;
155 }
156
157 //-------------------------------------------------------------------------
158 // mgmt_open_mode_elevate
159 //-------------------------------------------------------------------------
160
161 int
mgmt_open_mode_elevate(const char * path,int oflag,mode_t mode,bool elevate_p)162 mgmt_open_mode_elevate(const char *path, int oflag, mode_t mode, bool elevate_p)
163 {
164 int r, retries;
165 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
166 r = elevate_p ? elevating_open(path, oflag, mode) : ::open(path, oflag, mode);
167 if (r >= 0) {
168 return r;
169 }
170 if (!mgmt_transient_error()) {
171 break;
172 }
173 }
174 return r;
175 }
176 //-------------------------------------------------------------------------
177 // mgmt_select
178 //-------------------------------------------------------------------------
179
180 int
mgmt_select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * errorfds,struct timeval * timeout)181 mgmt_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
182 {
183 // Note: Linux select() has slight different semantics. From the
184 // man page: "On Linux, timeout is modified to reflect the amount of
185 // time not slept; most other implementations do not do this."
186 // Linux select() can also return ENOMEM, so we especially need to
187 // protect the call with the transient error retry loop.
188 // Fortunately, because of the Linux timeout handling, our
189 // mgmt_select call will still timeout correctly, rather than
190 // possibly extending our timeout period by up to
191 // MGMT_MAX_TRANSIENT_ERRORS times.
192 #if defined(linux)
193 int r, retries;
194 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
195 r = ::select(nfds, readfds, writefds, errorfds, timeout);
196 if (r >= 0) {
197 return r;
198 }
199 if (!mgmt_transient_error()) {
200 break;
201 }
202 }
203 return r;
204 #else
205 return ::select(nfds, readfds, writefds, errorfds, timeout);
206 #endif
207 }
208
209 //-------------------------------------------------------------------------
210 // mgmt_sendto
211 //-------------------------------------------------------------------------
212
213 int
mgmt_sendto(int fd,void * buf,int len,int flags,struct sockaddr * to,int tolen)214 mgmt_sendto(int fd, void *buf, int len, int flags, struct sockaddr *to, int tolen)
215 {
216 int r, retries;
217 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
218 r = ::sendto(fd, static_cast<char *>(buf), len, flags, to, tolen);
219 if (r >= 0) {
220 return r;
221 }
222 if (!mgmt_transient_error()) {
223 break;
224 }
225 }
226 return r;
227 }
228
229 //-------------------------------------------------------------------------
230 // mgmt_socket
231 //-------------------------------------------------------------------------
232
233 int
mgmt_socket(int domain,int type,int protocol)234 mgmt_socket(int domain, int type, int protocol)
235 {
236 int r, retries;
237 for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
238 r = ::socket(domain, type, protocol);
239 if (r >= 0) {
240 return r;
241 }
242 if (!mgmt_transient_error()) {
243 break;
244 }
245 }
246 return r;
247 }
248
249 /***************************************************************************
250 * mgmt_write_timeout
251 *
252 * purpose: checks if the specified socket is ready to be written too; only
253 * checks for the specified time
254 * input: fd - the socket to wait for
255 * sec - time to wait in secs
256 * usec - time to wait in usecs
257 * output: return 0 if time expires and the fd is not ready to be written
258 * return > 0 (actually 1) if fd is ready to be written
259 * return < 0 if error
260 ***************************************************************************/
261 int
mgmt_write_timeout(int fd,int sec,int usec)262 mgmt_write_timeout(int fd, int sec, int usec)
263 {
264 struct timeval timeout;
265 fd_set writeSet;
266 timeout.tv_sec = sec;
267 timeout.tv_usec = usec;
268
269 if (fd < 0 || fd >= FD_SETSIZE) {
270 errno = EBADF;
271 return -1;
272 }
273
274 FD_ZERO(&writeSet);
275 FD_SET(fd, &writeSet);
276
277 if (sec < 0 && usec < 0) {
278 // blocking select; only returns when fd is ready to write
279 return (mgmt_select(fd + 1, nullptr, &writeSet, nullptr, nullptr));
280 } else {
281 return (mgmt_select(fd + 1, nullptr, &writeSet, nullptr, &timeout));
282 }
283 }
284
285 /***************************************************************************
286 * mgmt_read_timeout
287 *
288 * purpose: need timeout for socket after sending a request and waiting to
289 * read reply check to see if anything to read;
290 * but only wait for fixed time specified in timeout struct
291 * input: fd - the socket to wait for
292 * sec - time to wait in secs
293 * usec - time to wait in usecs
294 * output: returns 0 if time expires and the fd is not ready
295 * return > 0 (actually 1) if fd is ready to read
296 * reason: the client could send a reply, but if TM is down or has
297 * problems sending a reply then the client could end up hanging,
298 * waiting to read a replay from the local side
299 ***************************************************************************/
300 int
mgmt_read_timeout(int fd,int sec,int usec)301 mgmt_read_timeout(int fd, int sec, int usec)
302 {
303 struct timeval timeout;
304 fd_set readSet;
305 timeout.tv_sec = sec;
306 timeout.tv_usec = usec;
307
308 if (fd < 0 || fd >= FD_SETSIZE) {
309 errno = EBADF;
310 return -1;
311 }
312
313 FD_ZERO(&readSet);
314 FD_SET(fd, &readSet);
315
316 return mgmt_select(fd + 1, &readSet, nullptr, nullptr, &timeout);
317 }
318
319 bool
mgmt_has_peereid()320 mgmt_has_peereid()
321 {
322 #if HAVE_GETPEEREID
323 return true;
324 #elif HAVE_GETPEERUCRED
325 return true;
326 #elif TS_HAS_SO_PEERCRED
327 return true;
328 #else
329 return false;
330 #endif
331 }
332
333 int
mgmt_get_peereid(int fd,uid_t * euid,gid_t * egid)334 mgmt_get_peereid(int fd, uid_t *euid, gid_t *egid)
335 {
336 *euid = -1;
337 *egid = -1;
338
339 #if HAVE_GETPEEREID
340 return getpeereid(fd, euid, egid);
341 #elif HAVE_GETPEERUCRED
342 ucred_t *ucred;
343
344 if (getpeerucred(fd, &ucred) == -1) {
345 return -1;
346 }
347
348 *euid = ucred_geteuid(ucred);
349 *egid = ucred_getegid(ucred);
350 ucred_free(ucred);
351 return 0;
352 #elif TS_HAS_SO_PEERCRED
353 struct ucred cred;
354 socklen_t credsz = sizeof(cred);
355 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &credsz) == -1) {
356 return -1;
357 }
358
359 *euid = cred.uid;
360 *egid = cred.gid;
361 return 0;
362 #else
363 (void)fd;
364 errno = ENOTSUP;
365 return -1;
366 #endif
367 }
368