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