1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <signal.h>
  27 #include <sys/types.h>
  28 #include <sys/time.h>
  29 #include <sys/socket.h>
  30 #include <netinet/in.h>
  31 #include <netinet/tcp.h>
  32 #include <netdb.h>
  33 #include <stdio.h>
  34 #include <string.h>
  35 #include <unistd.h>
  36 #include <stdlib.h>
  37 #include <errno.h>
  38 
  39 #include "cfg_lockd.h"
  40 
  41 static daemonaddr_t     clientaddr;
  42 static daemonaddr_t     server;
  43 
  44 static unsigned short   server_port = CFG_SERVER_PORT;
  45 static int      lock_soc = 0;
  46 static int      pf_inet = AF_INET;
  47 static int      locked;
  48 static int      initdone;
  49 static int      initresult;
  50 static pid_t    socket_pid;
  51 
  52 static void     cfg_lockd_reinit();
  53 
  54 static int last_cmd = -1;
  55 static uint8_t seq = 0;
  56 
  57 static void
  58 send_cmd(int cmd)
  59 {
  60         struct lock_msg message_buf;
  61         int rc;
  62 
  63         if (last_cmd == cmd) {
  64                 message_buf.seq = seq;
  65         } else {
  66                 message_buf.seq = ++seq;
  67                 last_cmd = cmd;
  68         }
  69         message_buf.message = cmd;
  70         if ((message_buf.pid = getpid()) != socket_pid)
  71                 cfg_lockd_reinit();
  72 
  73         do {
  74                 rc = sendto(lock_soc, &message_buf, sizeof (message_buf), 0,
  75                         (struct sockaddr *)&server, sizeof (server));
  76         } while (rc == -1 && errno == EINTR);
  77 #ifdef CFG_LOCKD_DEBUG
  78         if (rc < 0) {
  79                 perror("send");
  80         }
  81 #endif
  82 }
  83 
  84 static void
  85 read_msg(struct lock_msg *mp)
  86 {
  87         struct sockaddr from;
  88         int rc, len;
  89 
  90         /* wait for response */
  91         do {
  92                 struct pollfd fds;
  93 
  94                 fds.fd = lock_soc;
  95                 fds.events = POLLIN;
  96                 fds.revents = 0;
  97 
  98                 rc = poll(&fds, 1, 500);
  99                 if (!rc) {
 100 #ifdef CFG_LOCKD_DEBUG
 101                         fprintf(stderr, "LOCKD: resending last command (%d)\n",
 102                             last_cmd);
 103 #endif
 104                         send_cmd(last_cmd);
 105                 }
 106         } while (rc == 0 ||
 107                 (rc == -1 && errno == EINTR));
 108 
 109         do {
 110                 len = sizeof (from);
 111                 rc = recvfrom(lock_soc, mp, sizeof (*mp), 0,
 112                         &from, &len);
 113         } while (rc == -1 && errno == EINTR);
 114 #ifdef CFG_LOCKD_DEBUG
 115         if (rc < 0) {
 116                 perror("revcfrom");
 117         }
 118 #endif
 119 }
 120 
 121 static void
 122 read_reply()
 123 {
 124         struct lock_msg message_buf;
 125 
 126         do {
 127                 read_msg(&message_buf);
 128         } while (message_buf.seq != seq || message_buf.message != LOCK_LOCKED);
 129 }
 130 
 131 static void
 132 read_ack()
 133 {
 134         struct lock_msg message_buf;
 135 
 136         do {
 137                 read_msg(&message_buf);
 138         } while (message_buf.seq != seq || message_buf.message != LOCK_ACK);
 139 }
 140 
 141 void
 142 cfg_lockd_rdlock()
 143 {
 144 #ifdef CFG_LOCKD_DEBUG
 145         FILE *fp;
 146 #endif
 147 
 148         send_cmd(LOCK_READ);
 149         locked = 1;
 150         read_reply();
 151 
 152 #ifdef CFG_LOCKD_DEBUG
 153         fp = fopen("/tmp/locktag", "a");
 154         if (fp) {
 155                 time_t t = time(0);
 156                 fprintf(fp, "%19.19s read  lock acquired\n", ctime(&t));
 157                 fclose(fp);
 158         }
 159         sleep(3);
 160 #endif
 161 }
 162 
 163 void
 164 cfg_lockd_wrlock()
 165 {
 166 #ifdef CFG_LOCKD_DEBUG
 167         FILE *fp;
 168 #endif
 169 
 170         send_cmd(LOCK_WRITE);
 171         locked = 1;
 172         read_reply();
 173 
 174 #ifdef CFG_LOCKD_DEBUG
 175         fp = fopen("/tmp/locktag", "a");
 176         if (fp) {
 177                 time_t t = time(0);
 178                 fprintf(fp, "%19.19s write lock acquired\n", ctime(&t));
 179                 fclose(fp);
 180         }
 181         sleep(3);
 182 #endif
 183 }
 184 
 185 void
 186 cfg_lockd_unlock()
 187 {
 188 #ifdef CFG_LOCKD_DEBUG
 189         FILE *fp;
 190 #endif
 191 
 192         send_cmd(LOCK_NOTLOCKED);
 193         read_ack();
 194         locked = 0;
 195 
 196 #ifdef CFG_LOCKD_DEBUG
 197         fp = fopen("/tmp/locktag", "a");
 198         if (fp) {
 199                 time_t t = time(0);
 200                 fprintf(fp, "%19.19s ----- lock released\n", ctime(&t));
 201                 fclose(fp);
 202         }
 203         sleep(3);
 204 #endif
 205 }
 206 
 207 void
 208 cfg_lockd_stat()
 209 {
 210         send_cmd(LOCK_STAT);
 211 }
 212 
 213 cfglockd_t
 214 cfg_lockedby(pid_t *pidp)
 215 {
 216         struct lock_msg message_buf;
 217         send_cmd(LOCK_LOCKEDBY);
 218         read_msg(&message_buf);
 219         *pidp = message_buf.pid;
 220         return ((cfglockd_t)message_buf.message);
 221 }
 222 
 223 static void
 224 cfg_atexit()
 225 {
 226         if (locked)
 227                 cfg_lockd_unlock();
 228 }
 229 
 230 static int
 231 cfg_lockd_socket()
 232 {
 233         if ((lock_soc = socket(pf_inet, SOCK_DGRAM, 0)) < 0) {
 234 #ifdef CFG_LOCKD_DEBUG
 235                 fprintf(stderr, "libcfg: failed to create socket\n");
 236                 perror("socket");
 237 #endif
 238                 return (-1);
 239         }
 240         clientaddr.sin_family = AF_INET;
 241         clientaddr.sin_addr.s_addr = INADDR_ANY;
 242         clientaddr.sin_port = htons(0);
 243         if (bind(lock_soc, (struct sockaddr *)&clientaddr,
 244             sizeof (clientaddr)) < 0) {
 245 #ifdef CFG_LOCKD_DEBUG
 246                 perror("bind");
 247 #endif
 248                 return (-1);
 249         }
 250         socket_pid = getpid();
 251         return (0);
 252 }
 253 
 254 /*
 255  * Re-initialise after a fork has been detected.
 256  *
 257  * Needs to create a new socket for new process to receive messages
 258  * from the lock daemon and enter pid into lock file so that the daemon
 259  * can detect new processes exit if it doesn't call unlock first.
 260  */
 261 
 262 static void
 263 cfg_lockd_reinit()
 264 {
 265         if (lock_soc)
 266                 close(lock_soc);
 267         lock_soc = 0;
 268         if (cfg_lockd_socket()) {
 269                 initresult = 0;
 270                 return;
 271         }
 272         cfg_enterpid();
 273         initresult = 1;
 274 }
 275 
 276 int
 277 cfg_lockd_init()
 278 {
 279         struct  hostent *hp;
 280         FILE    *fp;
 281         int     pid = 0x12345678;
 282 
 283         if (initdone) {
 284                 /* only perform reinit if init worked first time */
 285                 if (getpid() != socket_pid && initresult != 0)
 286                         cfg_lockd_reinit();
 287                 return (initresult);
 288         }
 289 
 290         initdone = 1;
 291         initresult = 0;
 292 
 293         /* check if there's a lock daemon out there */
 294         if ((fp = fopen(CFG_PIDFILE, "r")) == NULL)
 295                 return (0);
 296         if (fscanf(fp, "%d\n", &pid) != 1) {
 297                 fclose(fp);
 298                 return (0);
 299         }
 300         fclose(fp);
 301         if (kill((pid_t)pid, 0) != 0)
 302                 return (0);
 303 
 304         /* there is a lock daemon */
 305         cfg_lfinit();
 306         cfg_enterpid();
 307         if (cfg_lockd_socket())
 308                 return (0);
 309 
 310         if ((hp = gethostbyname("localhost")) == NULL) {
 311 #ifdef CFG_LOCKD_DEBUG
 312                 fprintf(stderr, "Can't find hostent for %s\n", "localhost");
 313 #endif
 314                 return (0);
 315         }
 316         (void) memcpy(&(server.sin_addr.s_addr), *(hp->h_addr_list),
 317                                 sizeof (server.sin_addr));
 318         server.sin_port = htons(server_port);
 319         server.sin_family = hp->h_addrtype;
 320         endhostent();
 321         atexit(cfg_atexit);
 322         initresult = 1;
 323         return (1);
 324 }