Windows: Build Demand Attach File Service
[openafs.git] / src / vol / fssync-client.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  *
9  * Portions Copyright (c) 2006,2008 Sine Nomine Associates
10  */
11
12 /*
13         System:         VICE-TWO
14         Module:         fssync.c
15         Institution:    The Information Technology Center, Carnegie-Mellon University
16
17  */
18
19 #ifndef AFS_PTHREAD_ENV
20 #define USUAL_PRIORITY (LWP_MAX_PRIORITY - 2)
21
22 /*
23  * stack size increased from 8K because the HP machine seemed to have trouble
24  * with the smaller stack
25  */
26 #define USUAL_STACK_SIZE        (24 * 1024)
27 #endif /* !AFS_PTHREAD_ENV */
28
29 /*
30    fssync-client.c
31    File server synchronization with external volume utilities.
32    client-side implementation
33  */
34
35 #include <afsconfig.h>
36 #include <afs/param.h>
37
38
39 #include <sys/types.h>
40 #include <stdio.h>
41 #ifdef AFS_NT40_ENV
42 #include <winsock2.h>
43 #include <time.h>
44 #else
45 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netdb.h>
49 #include <sys/time.h>
50 #endif
51 #include <errno.h>
52 #ifdef AFS_PTHREAD_ENV
53 #include <assert.h>
54 #else /* AFS_PTHREAD_ENV */
55 #include <afs/assert.h>
56 #endif /* AFS_PTHREAD_ENV */
57 #include <signal.h>
58 #include <string.h>
59
60 #include <rx/xdr.h>
61 #include <afs/afsint.h>
62 #include "nfs.h"
63 #include <afs/errors.h>
64 #include "daemon_com.h"
65 #include "fssync.h"
66 #include "lwp.h"
67 #include "lock.h"
68 #include <afs/afssyscalls.h>
69 #include "ihandle.h"
70 #include "vnode.h"
71 #include "volume.h"
72 #include "partition.h"
73 #include "common.h"
74
75 #ifdef FSSYNC_BUILD_CLIENT
76
77 extern int LogLevel;
78
79 static SYNC_client_state fssync_state = 
80     { -1,                    /* file descriptor */
81       FSSYNC_ENDPOINT_DECL,  /* server endpoint */
82       FSYNC_PROTO_VERSION,   /* protocol version */
83       5,                     /* connect retry limit */
84       120,                   /* hard timeout */
85       "FSSYNC",              /* protocol name string */
86     };
87
88 #ifdef AFS_PTHREAD_ENV
89 static pthread_mutex_t vol_fsync_mutex;
90 static volatile int vol_fsync_mutex_init = 0;
91 #define VFSYNC_LOCK \
92     assert(pthread_mutex_lock(&vol_fsync_mutex) == 0)
93 #define VFSYNC_UNLOCK \
94     assert(pthread_mutex_unlock(&vol_fsync_mutex) == 0)
95 #else
96 #define VFSYNC_LOCK
97 #define VFSYNC_UNLOCK
98 #endif
99
100 int
101 FSYNC_clientInit(void)
102 {
103 #ifdef AFS_PTHREAD_ENV
104     /* this is safe since it gets called with VOL_LOCK held, or before we go multithreaded */
105     if (!vol_fsync_mutex_init) {
106         assert(pthread_mutex_init(&vol_fsync_mutex, NULL) == 0);
107         vol_fsync_mutex_init = 1;
108     }
109 #endif
110     return SYNC_connect(&fssync_state);
111 }
112
113 void
114 FSYNC_clientFinis(void)
115 {
116     SYNC_closeChannel(&fssync_state);
117 }
118
119 int
120 FSYNC_clientChildProcReconnect(void)
121 {
122     return SYNC_reconnect(&fssync_state);
123 }
124
125 /* fsync client interface */
126 afs_int32
127 FSYNC_askfs(SYNC_command * com, SYNC_response * res)
128 {
129     afs_int32 code;
130
131     VFSYNC_LOCK;
132     code = SYNC_ask(&fssync_state, com, res);
133     VFSYNC_UNLOCK;
134
135     switch (code) {
136     case SYNC_OK:
137     case SYNC_FAILED:
138         break;
139     case SYNC_COM_ERROR:
140     case SYNC_BAD_COMMAND:
141         Log("FSYNC_askfs: fatal FSSYNC protocol error; volume management functionality disabled until next fileserver restart\n");
142         break;
143     case SYNC_DENIED:
144         Log("FSYNC_askfs: FSSYNC request denied for reason=%d\n", res->hdr.reason);
145         break;
146     default:
147         Log("FSYNC_askfs: unknown protocol response %d\n", code);
148         break;
149     }
150     return code;
151 }
152
153
154 /**
155  *  FSSYNC volume operations client interface.
156  *
157  * @param[in]    volume     volume id
158  * @param[in]    partName   partition name string
159  * @param[in]    com        FSSYNC command code
160  * @param[in]    reason     FSSYNC reason sub-code
161  * @param[out]   res        response message
162  *
163  * @return operation status
164  *    @retval SYNC_OK  success
165  */
166 afs_int32
167 FSYNC_GenericOp(void * ext_hdr, size_t ext_len,
168               int command, int reason,
169               SYNC_response * res_in)
170 {
171     SYNC_response res_l, *res;
172     SYNC_command com;
173
174     if (res_in) {
175         res = res_in;
176     } else {
177         res = &res_l;
178         res_l.payload.buf = NULL;
179         res_l.payload.len = 0;
180     }
181
182     memset(&com, 0, sizeof(com));
183
184     com.hdr.programType = programType;
185     com.hdr.command = command;
186     com.hdr.reason = reason;
187     com.hdr.command_len = sizeof(com.hdr) + ext_len;
188     com.payload.buf = ext_hdr;
189     com.payload.len = ext_len;
190
191     return FSYNC_askfs(&com, res);
192 }
193
194 afs_int32
195 FSYNC_VolOp(VolumeId volume, char * partition, 
196             int command, int reason,
197             SYNC_response * res)
198 {
199     FSSYNC_VolOp_hdr vcom;
200
201     memset(&vcom, 0, sizeof(vcom));
202
203     vcom.volume = volume;
204     if (partition)
205         strlcpy(vcom.partName, partition, sizeof(vcom.partName));
206
207     return FSYNC_GenericOp(&vcom, sizeof(vcom), command, reason, res);
208 }
209
210 /**
211  * verify that the fileserver still thinks we have a volume checked out.
212  *
213  * In DAFS, a non-fileserver program accesses a volume by checking it out from
214  * the fileserver (FSYNC_VOL_OFF or FSYNC_VOL_NEEDVOLUME), and then locks the
215  * volume. There is a possibility that the fileserver crashes or restarts for
216  * some reason between volume checkout and locking; if this happens, the
217  * fileserver could attach the volume before we had a chance to lock it. This
218  * function serves to detect if this has happened; it must be called after
219  * volume checkout and locking to make sure the fileserver still thinks we
220  * have the volume. (If it doesn't, we should try to check it out again.)
221  *
222  * @param[in] volume    volume ID
223  * @param[in] partition partition name string
224  * @param[in] command   the command that was used to checkout the volume
225  * @param[in] reason    the reason code used to checkout the volume
226  *
227  * @return operation status
228  *  @retval SYNC_OK the fileserver could not have attached the volume since
229  *                  it was checked out (either it thinks it is still checked
230  *                  out, or it doesn't know about the volume)
231  *  @retval SYNC_DENIED fileserver may have restarted since checkout; checkout
232  *                      should be reattempted
233  *  @retval SYNC_COM_ERROR internal/fatal error
234  */
235 afs_int32
236 FSYNC_VerifyCheckout(VolumeId volume, char * partition,
237                      afs_int32 command, afs_int32 reason)
238 {
239     SYNC_response res;
240     FSSYNC_VolOp_info vop;
241     afs_int32 code;
242     afs_int32 pid;
243
244     res.hdr.response_len = sizeof(res.hdr);
245     res.payload.buf = &vop;
246     res.payload.len = sizeof(vop);
247
248     code = FSYNC_VolOp(volume, partition, FSYNC_VOL_QUERY_VOP, FSYNC_WHATEVER, &res);
249     if (code != SYNC_OK) {
250         if (res.hdr.reason == FSYNC_NO_PENDING_VOL_OP) {
251             Log("FSYNC_VerifyCheckout: fileserver claims no vop for vol %lu "
252                 "part %s; fileserver may have restarted since checkout\n",
253                 afs_printable_uint32_lu(volume), partition);
254             return SYNC_DENIED;
255         }
256
257         if (res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
258             /* if the fileserver does not know about this volume, there's no
259              * way it could have attached it, so we're fine */
260             return SYNC_OK;
261         }
262
263         Log("FSYNC_VerifyCheckout: FSYNC_VOL_QUERY_VOP failed for vol %lu "
264             "part %s with code %ld reason %ld\n",
265             afs_printable_uint32_lu(volume), partition,
266             afs_printable_int32_ld(code),
267             afs_printable_int32_ld(res.hdr.reason));
268         return SYNC_COM_ERROR;
269     }
270
271     pid = getpid();
272
273     /* Check if the current vol op is us. Checking pid is probably enough, but
274      * be a little bit paranoid. We could also probably check tid, but I'm not
275      * completely confident of its reliability on all platforms (on pthread
276      * envs, we coerce a pthread_t to an afs_int32, which is not guaranteed
277      * to mean anything significant). */
278
279     if (vop.com.programType == programType && vop.com.pid == pid &&
280         vop.com.command == command && vop.com.reason == reason) {
281
282         /* looks like the current pending vol op is the same one as the one
283          * with which we checked it out. success. */
284         return SYNC_OK;
285     }
286
287     Log("FSYNC_VerifyCheckout: vop for vol %lu part %s does not match "
288         "expectations (got pt %ld pid %ld cmd %ld reason %ld, but expected "
289         "pt %ld pid %ld cmd %ld reason %ld); fileserver may have restarted "
290         "since checkout\n", afs_printable_uint32_lu(volume), partition,
291         afs_printable_int32_ld(vop.com.programType),
292         afs_printable_int32_ld(vop.com.pid),
293         afs_printable_int32_ld(vop.com.command),
294         afs_printable_int32_ld(vop.com.reason),
295         afs_printable_int32_ld(programType),
296         afs_printable_int32_ld(pid),
297         afs_printable_int32_ld(command),
298         afs_printable_int32_ld(reason));
299
300     return SYNC_DENIED;
301 }
302
303 afs_int32
304 FSYNC_StatsOp(FSSYNC_StatsOp_hdr * scom, int command, int reason,
305               SYNC_response * res)
306 {
307     return FSYNC_GenericOp(scom, sizeof(*scom), command, reason, res);
308 }
309
310 /**
311  * query the volume group cache.
312  *
313  * @param[in]  part     vice partition path
314  * @param[in]  volid    volume id
315  * @param[out] qry      query response object
316  * @param[out] res      SYNC response message
317  *
318  * @return operation status
319  *    @retval SYNC_OK success
320  */
321 afs_int32
322 FSYNC_VGCQuery(char * part,
323              VolumeId volid,
324              FSSYNC_VGQry_response_t * qry,
325              SYNC_response *res)
326 {
327     SYNC_response lres;
328
329     if (!res) {
330         res = &lres;
331     }
332
333     res->hdr.response_len = sizeof(res->hdr);
334     res->payload.buf = qry;
335     res->payload.len = sizeof(*qry);
336
337     return FSYNC_VolOp(volid, part, FSYNC_VG_QUERY, 0, res);
338 }
339
340 /**
341  * perform an update operation on the VGC.
342  *
343  * @param[in] parent    rw volume
344  * @param[in] child     volume id to add
345  * @param[in] partition name of vice partition on which this VG resides
346  * @param[in] opcode    FSSYNC VG cache opcode
347  * @param[in] reason    FSSYNC reason code
348  * @param[out] res      SYNC response message
349  *
350  * @return operation status
351  *    @retval SYNC_OK success
352  *
353  * @internal
354  */
355 static afs_int32
356 _FSYNC_VGCUpdate(char * partition,
357                  VolumeId parent,
358                  VolumeId child,
359                  int opcode,
360                  int reason,
361                  SYNC_response *res)
362 {
363     FSSYNC_VGUpdate_command_t vcom;
364
365     memset(&vcom, 0, sizeof(vcom));
366
367     vcom.parent = parent;
368     vcom.child = child;
369     if (partition)
370         strlcpy(vcom.partName, partition, sizeof(vcom.partName));
371
372     return FSYNC_GenericOp(&vcom, sizeof(vcom), opcode, reason, res);
373 }
374
375 /**
376  * Add volume to volume group cache.
377  *
378  * @param[in] parent    rw volume
379  * @param[in] child     volume id to add
380  * @param[in] partition name of vice partition on which this VG resides
381  * @param[in] reason    FSSYNC reason code
382  * @param[out] res      SYNC response message
383  *
384  * @return operation status
385  *    @retval SYNC_OK success
386  */
387 afs_int32
388 FSYNC_VGCAdd(char * partition,
389              VolumeId parent,
390              VolumeId child,
391              int reason,
392              SYNC_response *res)
393 {
394     return _FSYNC_VGCUpdate(partition, parent, child, FSYNC_VG_ADD, reason, res);
395 }
396
397 /**
398  * Delete volume from volume group cache.
399  *
400  * @param[in] parent    rw volume
401  * @param[in] child     volume id to add
402  * @param[in] partition name of vice partition on which this VG resides
403  * @param[in] reason    FSSYNC reason code
404  * @param[out] res      SYNC response message
405  *
406  * @return operation status
407  *    @retval SYNC_OK success
408  */
409 afs_int32
410 FSYNC_VGCDel(char * partition,
411              VolumeId parent,
412              VolumeId child,
413              int reason,
414              SYNC_response *res)
415 {
416     return _FSYNC_VGCUpdate(partition, parent, child, FSYNC_VG_DEL, reason, res);
417 }
418
419 /**
420  * perform an asynchronous volume group scan.
421  *
422  * @param[in] partition   vice partition string
423  * @param[in] reason      FSSYNC reason code
424  *
425  * @note if partition is NULL, all vice partitions will be scanned.
426  *
427  * @return operation status
428  *    @retval SYNC_OK success
429  */
430 afs_int32
431 FSYNC_VGCScan(char * partition, int reason)
432 {
433     int command;
434
435     if (partition == NULL) {
436         command = FSYNC_VG_SCAN_ALL;
437         partition = "";
438     } else {
439         command = FSYNC_VG_SCAN;
440     }
441
442     return FSYNC_VolOp(0, partition, command, reason, NULL);
443 }
444
445 #endif /* FSSYNC_BUILD_CLIENT */