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