1 /* Copyright (C) 2009 Pierre-Marc Fournier
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include <sys/types.h>
22 #include <sys/socket.h>
37 #define UNIX_PATH_MAX 108
41 /* FIXME: ustcomm blocks on message sending, which might be problematic in
42 * some cases. Fix the poll() usage so sends are buffered until they don't
46 //static void bt(void)
51 // result = backtrace(&buffer, 100);
52 // backtrace_symbols_fd(buffer, result, STDERR_FILENO);
55 static int mkdir_p(const char *path
, mode_t mode
)
63 tmp
= malloc(strlen(path
) + 1);
71 while (*path_p
!= '/') {
77 strncpy(tmp
, path
, path_p
- path
);
78 tmp
[path_p
-path
] = '\0';
79 if (tmp
[path_p
- path
- 1] != '/') {
80 result
= mkdir(tmp
, mode
);
82 if (!(errno
== EEXIST
|| errno
== EACCES
|| errno
== EROFS
)) {
83 /* Then this is a real error */
93 result
= mkdir(path
, mode
);
104 char *strdup_malloc(const char *s
)
111 retval
= (char *) malloc(strlen(s
)+1);
118 static int signal_process(pid_t pid
)
123 int pid_is_online(pid_t pid
) {
129 * @fd: file descriptor to send to
130 * @msg: a null-terminated string containing the message to send
134 * 0: connection closed
138 static int send_message_fd(int fd
, const char *msg
)
142 /* Send including the final \0 */
143 result
= patient_send(fd
, msg
, strlen(msg
)+1, MSG_NOSIGNAL
);
149 else if(result
== 0) {
153 DBG("sent message \"%s\"", msg
);
157 /* Called by an app to ask the consumer daemon to connect to it. */
159 int ustcomm_request_consumer(pid_t pid
, const char *channel
)
161 char path
[UNIX_PATH_MAX
];
165 struct ustcomm_connection conn
;
166 char *explicit_daemon_socket_path
;
168 explicit_daemon_socket_path
= getenv("UST_DAEMON_SOCKET");
169 if(explicit_daemon_socket_path
) {
170 /* user specified explicitly a socket path */
171 result
= snprintf(path
, UNIX_PATH_MAX
, "%s", explicit_daemon_socket_path
);
174 /* just use the default path */
175 result
= snprintf(path
, UNIX_PATH_MAX
, "%s/ustd", SOCK_DIR
);
178 if(result
>= UNIX_PATH_MAX
) {
179 ERR("string overflow allocating socket name");
183 asprintf(&msg
, "collect %d %s", pid
, channel
);
185 /* don't signal it because it's the daemon */
186 result
= ustcomm_connect_path(path
, &conn
, -1);
188 WARN("ustcomm_connect_path failed");
193 result
= ustcomm_send_request(&conn
, msg
, NULL
);
195 WARN("ustcomm_send_request failed");
201 ustcomm_disconnect(&conn
);
208 /* returns 1 to indicate a message was received
209 * returns 0 to indicate no message was received (end of stream)
210 * returns -1 to indicate an error
213 #define RECV_INCREMENT 1
214 #define RECV_INITIAL_BUF_SIZE 10
216 static int recv_message_fd(int fd
, char **msg
)
219 int buf_alloc_size
= 0;
221 int buf_used_size
= 0;
223 buf
= malloc(RECV_INITIAL_BUF_SIZE
);
224 buf_alloc_size
= RECV_INITIAL_BUF_SIZE
;
227 if(buf_used_size
+ RECV_INCREMENT
> buf_alloc_size
) {
230 new_buf
= (char *) realloc(buf
, buf_alloc_size
);
231 if(new_buf
== NULL
) {
232 ERR("realloc returned NULL");
239 /* FIXME: this is really inefficient; but with count>1 we would
240 * need a buffering mechanism */
241 result
= recv(fd
, buf
+buf_used_size
, RECV_INCREMENT
, 0);
244 if(errno
!= ECONNRESET
)
257 buf_used_size
+= result
;
259 if(buf
[buf_used_size
-1] == 0) {
266 DBG("received message \"%s\"", buf
);
272 int ustcomm_send_reply(struct ustcomm_server
*server
, char *msg
, struct ustcomm_source
*src
)
276 result
= send_message_fd(src
->fd
, msg
);
278 ERR("error in send_message_fd");
285 /* Called after a fork. */
287 int ustcomm_close_all_connections(struct ustcomm_server
*server
)
289 struct ustcomm_connection
*conn
;
290 struct ustcomm_connection
*deletable_conn
= NULL
;
292 list_for_each_entry(conn
, &server
->connections
, list
) {
293 free(deletable_conn
);
294 deletable_conn
= conn
;
296 list_del(&conn
->list
);
302 /* @timeout: max blocking time in milliseconds, -1 means infinity
304 * returns 1 to indicate a message was received
305 * returns 0 to indicate no message was received
306 * returns -1 to indicate an error
309 int ustcomm_recv_message(struct ustcomm_server
*server
, char **msg
, struct ustcomm_source
*src
, int timeout
)
312 struct ustcomm_connection
*conn
;
320 list_for_each_entry(conn
, &server
->connections
, list
) {
324 fds
= (struct pollfd
*) malloc(n_fds
* sizeof(struct pollfd
));
326 ERR("malloc returned NULL");
330 /* special idx 0 is for listening socket */
331 fds
[idx
].fd
= server
->listen_fd
;
332 fds
[idx
].events
= POLLIN
;
335 list_for_each_entry(conn
, &server
->connections
, list
) {
336 fds
[idx
].fd
= conn
->fd
;
337 fds
[idx
].events
= POLLIN
;
341 while((result
= poll(fds
, n_fds
, timeout
)) == -1 && errno
== EINTR
)
352 struct ustcomm_connection
*newconn
;
355 result
= newfd
= accept(server
->listen_fd
, NULL
, NULL
);
361 newconn
= (struct ustcomm_connection
*) malloc(sizeof(struct ustcomm_connection
));
362 if(newconn
== NULL
) {
363 ERR("malloc returned NULL");
369 list_add(&newconn
->list
, &server
->connections
);
372 for(idx
=1; idx
<n_fds
; idx
++) {
373 if(fds
[idx
].revents
) {
374 retval
= recv_message_fd(fds
[idx
].fd
, msg
);
376 src
->fd
= fds
[idx
].fd
;
379 /* connection finished */
382 list_for_each_entry(conn
, &server
->connections
, list
) {
383 if(conn
->fd
== fds
[idx
].fd
) {
384 list_del(&conn
->list
);
390 goto free_fds_return
;
403 int ustcomm_ustd_recv_message(struct ustcomm_ustd
*ustd
, char **msg
, struct ustcomm_source
*src
, int timeout
)
405 return ustcomm_recv_message(&ustd
->server
, msg
, src
, timeout
);
408 int ustcomm_app_recv_message(struct ustcomm_app
*app
, char **msg
, struct ustcomm_source
*src
, int timeout
)
410 return ustcomm_recv_message(&app
->server
, msg
, src
, timeout
);
413 /* This removes src from the list of active connections of app.
416 int ustcomm_app_detach_client(struct ustcomm_app
*app
, struct ustcomm_source
*src
)
418 struct ustcomm_server
*server
= (struct ustcomm_server
*)app
;
419 struct ustcomm_connection
*conn
;
421 list_for_each_entry(conn
, &server
->connections
, list
) {
422 if(conn
->fd
== src
->fd
) {
423 list_del(&conn
->list
);
433 static int init_named_socket(const char *name
, char **path_out
)
438 struct sockaddr_un addr
;
440 result
= fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
446 addr
.sun_family
= AF_UNIX
;
448 strncpy(addr
.sun_path
, name
, UNIX_PATH_MAX
);
449 addr
.sun_path
[UNIX_PATH_MAX
-1] = '\0';
451 result
= access(name
, F_OK
);
454 result
= unlink(name
);
456 PERROR("unlink of socket file");
459 WARN("socket already exists; overwriting");
462 result
= bind(fd
, (struct sockaddr
*)&addr
, sizeof(addr
));
468 result
= listen(fd
, 1);
475 *path_out
= strdup(addr
.sun_path
);
488 * 0: Success, but no reply because recv() returned 0
492 * On error, the error message is printed, except on
493 * ECONNRESET, which is normal when the application dies.
496 int ustcomm_send_request(struct ustcomm_connection
*conn
, const char *req
, char **reply
)
500 /* Send including the final \0 */
501 result
= send_message_fd(conn
->fd
, req
);
508 result
= recv_message_fd(conn
->fd
, reply
);
512 else if(result
== 0) {
519 int ustcomm_connect_path(const char *path
, struct ustcomm_connection
*conn
, pid_t signalpid
)
523 struct sockaddr_un addr
;
525 result
= fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
531 addr
.sun_family
= AF_UNIX
;
533 result
= snprintf(addr
.sun_path
, UNIX_PATH_MAX
, "%s", path
);
534 if(result
>= UNIX_PATH_MAX
) {
535 ERR("string overflow allocating socket name");
540 result
= signal_process(signalpid
);
542 ERR("could not signal process");
547 result
= connect(fd
, (struct sockaddr
*)&addr
, sizeof(addr
));
549 PERROR("connect (path=%s)", path
);
558 int ustcomm_disconnect(struct ustcomm_connection
*conn
)
560 return close(conn
->fd
);
563 int ustcomm_connect_app(pid_t pid
, struct ustcomm_connection
*conn
)
566 char path
[UNIX_PATH_MAX
];
569 result
= snprintf(path
, UNIX_PATH_MAX
, "%s/%d", SOCK_DIR
, pid
);
570 if(result
>= UNIX_PATH_MAX
) {
571 ERR("string overflow allocating socket name");
575 return ustcomm_connect_path(path
, conn
, pid
);
578 static int ensure_dir_exists(const char *dir
)
586 result
= stat(dir
, &st
);
587 if(result
== -1 && errno
!= ENOENT
) {
590 else if(result
== -1) {
594 result
= mkdir_p(dir
, 0777);
596 ERR("executing in recursive creation of directory %s", dir
);
604 /* Called by an application to initialize its server so daemons can
608 int ustcomm_init_app(pid_t pid
, struct ustcomm_app
*handle
)
613 result
= asprintf(&name
, "%s/%d", SOCK_DIR
, (int)pid
);
614 if(result
>= UNIX_PATH_MAX
) {
615 ERR("string overflow allocating socket name");
619 result
= ensure_dir_exists(SOCK_DIR
);
621 ERR("Unable to create socket directory %s", SOCK_DIR
);
625 handle
->server
.listen_fd
= init_named_socket(name
, &(handle
->server
.socketpath
));
626 if(handle
->server
.listen_fd
< 0) {
627 ERR("Error initializing named socket (%s). Check that directory exists and that it is writable.", name
);
632 INIT_LIST_HEAD(&handle
->server
.connections
);
641 /* Used by the daemon to initialize its server so applications
645 int ustcomm_init_ustd(struct ustcomm_ustd
*handle
, const char *sock_path
)
651 asprintf(&name
, "%s", sock_path
);
656 /* Only check if socket dir exists if we are using the default directory */
657 result
= ensure_dir_exists(SOCK_DIR
);
659 ERR("Unable to create socket directory %s", SOCK_DIR
);
663 asprintf(&name
, "%s/%s", SOCK_DIR
, "ustd");
666 handle
->server
.listen_fd
= init_named_socket(name
, &handle
->server
.socketpath
);
667 if(handle
->server
.listen_fd
< 0) {
668 ERR("error initializing named socket at %s", name
);
673 INIT_LIST_HEAD(&handle
->server
.connections
);
681 void ustcomm_fini_app(struct ustcomm_app
*handle
)
687 result
= stat(handle
->server
.socketpath
, &st
);
689 PERROR("stat (%s)", handle
->server
.socketpath
);
693 /* Paranoid check before deleting. */
694 result
= S_ISSOCK(st
.st_mode
);
696 ERR("The socket we are about to delete is not a socket.");
700 result
= unlink(handle
->server
.socketpath
);
706 static const char *find_tok(const char *str
)
718 static const char *find_sep(const char *str
)
730 int nth_token_is(const char *str
, const char *token
, int tok_no
)
736 for(i
=0; i
<=tok_no
; i
++) {
750 if(end
-start
!= strlen(token
))
753 if(strncmp(start
, token
, end
-start
))
759 char *nth_token(const char *str
, int tok_no
)
761 static char *retval
= NULL
;
766 for(i
=0; i
<=tok_no
; i
++) {
785 asprintf(&retval
, "%.*s", (int)(end
-start
), start
);