1/* 2 * All rights reserved. See COPYRIGHT. 3 * 4 * IPC over socketpair between parent and children. 5 */ 6 7#ifdef HAVE_CONFIG_H 8#include "config.h" 9#endif 10 11#include <sys/types.h> 12#ifdef HAVE_UNISTD_H 13#include <unistd.h> 14#endif 15#include <stdio.h> 16#include <stdlib.h> 17#include <string.h> 18#include <sys/socket.h> 19#include <sys/un.h> 20#include <errno.h> 21#include <signal.h> 22#include <time.h> 23 24#include <atalk/server_child.h> 25#include <atalk/server_ipc.h> 26#include <atalk/logger.h> 27#include <atalk/util.h> 28#include <atalk/errchk.h> 29#include <atalk/paths.h> 30#include <atalk/globals.h> 31#include <atalk/dsi.h> 32 33#define IPC_HEADERLEN 14 34#define IPC_MAXMSGSIZE 90 35 36typedef struct ipc_header { 37 uint16_t command; 38 pid_t child_pid; 39 uid_t uid; 40 uint32_t len; 41 char *msg; 42 int afp_socket; 43 uint16_t DSI_requestID; 44} ipc_header_t; 45 46static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION", 47 "IPC_GETSESSION"}; 48 49/* 50 * Pass afp_socket to old disconnected session if one has a matching token (token = pid) 51 * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed 52 */ 53static int ipc_kill_token(struct ipc_header *ipc, server_child *children) 54{ 55 pid_t pid; 56 57 if (ipc->len != sizeof(pid_t)) { 58 return -1; 59 } 60 /* assume signals SA_RESTART set */ 61 memcpy (&pid, ipc->msg, sizeof(pid_t)); 62 63 return server_child_transfer_session(children, 64 CHILD_DSIFORK, 65 pid, 66 ipc->uid, 67 ipc->afp_socket, 68 ipc->DSI_requestID); 69} 70 71/* ----------------- */ 72static int ipc_get_session(struct ipc_header *ipc, server_child *children) 73{ 74 u_int32_t boottime; 75 u_int32_t idlen; 76 char *clientid, *p; 77 78 79 if (ipc->len < (sizeof(idlen) + sizeof(boottime)) ) 80 return -1; 81 82 p = ipc->msg; 83 memcpy (&idlen, p, sizeof(idlen)); 84 idlen = ntohl (idlen); 85 p += sizeof(idlen); 86 87 memcpy (&boottime, p, sizeof(boottime)); 88 p += sizeof(boottime); 89 90 if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime)) 91 return -1; 92 93 if (NULL == (clientid = (char*) malloc(idlen)) ) 94 return -1; 95 memcpy (clientid, p, idlen); 96 97 LOG(log_debug, logtype_afpd, "ipc_get_session(pid: %u, uid: %u, time: 0x%08x)", 98 ipc->child_pid, ipc->uid, boottime); 99 100 server_child_kill_one_by_id(children, 101 CHILD_DSIFORK, 102 ipc->child_pid, 103 ipc->uid, 104 idlen, 105 clientid, 106 boottime); 107 108 return 0; 109} 110 111/*********************************************************************************** 112 * Public functions 113 ***********************************************************************************/ 114 115/*! 116 * Listen on UNIX domain socket "name" for IPC from old sesssion 117 * 118 * @args name (r) file name to use for UNIX domain socket 119 * @returns socket fd, -1 on error 120 */ 121int ipc_server_uds(const char *name) 122{ 123 EC_INIT; 124 struct sockaddr_un address; 125 socklen_t address_length; 126 int fd = -1; 127 128 EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) ); 129 EC_ZERO_LOG( setnonblock(fd, 1) ); 130 unlink(name); 131 address.sun_family = AF_UNIX; 132 address_length = sizeof(address.sun_family) + sprintf(address.sun_path, name); 133 EC_ZERO_LOG( bind(fd, (struct sockaddr *)&address, address_length) ); 134 EC_ZERO_LOG( listen(fd, 1024) ); 135 136EC_CLEANUP: 137 if (ret != 0) { 138 return -1; 139 } 140 141 return fd; 142} 143 144/*! 145 * Connect to UNIX domain socket "name" for IPC with new afpd master 146 * 147 * 1. Connect 148 * 2. send pid, which establishes a child structure for us in the master 149 * 150 * @args name (r) file name to use for UNIX domain socket 151 * @returns socket fd, -1 on error 152 */ 153int ipc_client_uds(const char *name) 154{ 155 EC_INIT; 156 struct sockaddr_un address; 157 socklen_t address_length; 158 int fd = -1; 159 pid_t pid = getpid(); 160 161 EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) ); 162 EC_ZERO_LOG( setnonblock(fd, 1) ); 163 address.sun_family = AF_UNIX; 164 address_length = sizeof(address.sun_family) + sprintf(address.sun_path, name); 165 166 EC_ZERO_LOG( connect(fd, (struct sockaddr *)&address, address_length) ); /* 1 */ 167 LOG(log_debug, logtype_afpd, "ipc_client_uds: connected to master"); 168 169 if (writet(fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) { 170 LOG(log_error, logtype_afpd, "ipc_client_uds: writet: %s", strerror(errno)); 171 EC_FAIL; 172 } 173 174EC_CLEANUP: 175 if (ret != 0) { 176 return -1; 177 } 178 LOG(log_debug, logtype_afpd, "ipc_client_uds: fd: %d", fd); 179 return fd; 180} 181 182int reconnect_ipc(AFPObj *obj) 183{ 184 int retrycount = 0; 185 186 LOG(log_debug, logtype_afpd, "reconnect_ipc: start"); 187 188 close(obj->ipc_fd); 189 obj->ipc_fd = -1; 190 191 srandom(getpid()); 192 sleep((random() % 5) + 5); /* give it enough time to start */ 193 194 while (retrycount++ < 10) { 195 if ((obj->ipc_fd = ipc_client_uds(_PATH_AFP_IPC)) == -1) { 196 LOG(log_error, logtype_afpd, "reconnect_ipc: cant reconnect to master"); 197 sleep(1); 198 continue; 199 } 200 LOG(log_debug, logtype_afpd, "reconnect_ipc: succesfull IPC reconnect"); 201 return 0; 202 } 203 return -1; 204} 205 206/* ----------------- 207 * Ipc format 208 * command 209 * pid 210 * uid 211 * 212 */ 213 214/*! 215 * Read a IPC message from a child 216 * 217 * @args children (rw) pointer to our structure with all childs 218 * @args fd (r) IPC socket with child 219 * 220 * @returns number of bytes transfered, -1 on error, 0 on EOF 221 */ 222int ipc_server_read(server_child *children, int fd) 223{ 224 int ret = 0; 225 struct ipc_header ipc; 226 char buf[IPC_MAXMSGSIZE], *p; 227 228 if ((ret = read(fd, buf, IPC_HEADERLEN)) != IPC_HEADERLEN) { 229 LOG(log_error, logtype_afpd, "Reading IPC header failed (%i of %u bytes read): %s", 230 ret, IPC_HEADERLEN, strerror(errno)); 231 return ret; 232 } 233 234 p = buf; 235 236 memcpy(&ipc.command, p, sizeof(ipc.command)); 237 p += sizeof(ipc.command); 238 239 memcpy(&ipc.child_pid, p, sizeof(ipc.child_pid)); 240 p += sizeof(ipc.child_pid); 241 242 memcpy(&ipc.uid, p, sizeof(ipc.uid)); 243 p += sizeof(ipc.uid); 244 245 memcpy(&ipc.len, p, sizeof(ipc.len)); 246 247 /* This should never happen */ 248 if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN)) { 249 LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len); 250 return -1; 251 } 252 253 memset (buf, 0, IPC_MAXMSGSIZE); 254 if ( ipc.len != 0) { 255 if ((ret = read(fd, buf, ipc.len)) != (int) ipc.len) { 256 LOG(log_info, logtype_afpd, "Reading IPC message failed (%u of %u bytes read): %s", 257 ret, ipc.len, strerror(errno)); 258 return ret; 259 } 260 } 261 ipc.msg = buf; 262 263 LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u", 264 ipc_cmd_str[ipc.command], ipc.child_pid); 265 266 int afp_socket; 267 268 switch (ipc.command) { 269 270 case IPC_DISCOLDSESSION: 271 if (readt(fd, &ipc.DSI_requestID, 2, 0, 2) != 2) { 272 LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): couldnt read DSI id: %s", 273 ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno)); 274 } 275 if ((ipc.afp_socket = recv_fd(fd, 1)) == -1) { 276 LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): recv_fd: %s", 277 ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno)); 278 return -1; 279 } 280 if (ipc_kill_token(&ipc, children) == 1) { 281 /* Transfered session (ie afp_socket) to old disconnected child, now kill the new one */ 282 LOG(log_note, logtype_afpd, "Reconnect: killing new session child[%u] after transfer", 283 ipc.child_pid); 284 kill(ipc.child_pid, SIGTERM); 285 } 286 close(ipc.afp_socket); 287 break; 288 289 case IPC_GETSESSION: 290 if (ipc_get_session(&ipc, children) != 0) 291 return -1; 292 break; 293 294 default: 295 LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command); 296 return -1; 297 } 298 299 return ret; 300} 301 302/* ----------------- */ 303int ipc_child_write(int fd, uint16_t command, int len, void *msg) 304{ 305 char block[IPC_MAXMSGSIZE], *p; 306 pid_t pid; 307 uid_t uid; 308 ssize_t ret; 309 310 p = block; 311 312 memset ( p, 0 , IPC_MAXMSGSIZE); 313 if (len + IPC_HEADERLEN > IPC_MAXMSGSIZE) 314 return -1; 315 316 memcpy(p, &command, sizeof(command)); 317 p += sizeof(command); 318 319 pid = getpid(); 320 memcpy(p, &pid, sizeof(pid_t)); 321 p += sizeof(pid_t); 322 323 /* FIXME 324 * using uid is wrong. It will not disconnect if the new connection 325 * is with a different user. 326 * But we really don't want a remote kill command. 327 */ 328 uid = geteuid(); 329 memcpy(p, &uid, sizeof(uid_t)); 330 p += sizeof(uid_t); 331 332 memcpy(p, &len, 4); 333 p += 4; 334 335 memcpy(p, msg, len); 336 337 LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]); 338 339 if ((ret = writet(fd, block, len+IPC_HEADERLEN, 0, 1)) != len + IPC_HEADERLEN) { 340 return -1; 341 } 342 343 return 0; 344} 345 346