Whamcloud - gitweb
d9c7af01c71eb042aeb37e2ed1338106da715696
[fs/lustre-release.git] / lustre / utils / liblustreapi_chlg.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * (C) Copyright 2017 Commissariat a l'energie atomique et aux energies
7  *     alternatives
8  *
9  * All rights reserved. This program and the accompanying materials
10  * are made available under the terms of the GNU Lesser General Public License
11  * (LGPL) version 2.1 or (at your discretion) any later version.
12  * (LGPL) version 2.1 accompanies this distribution, and is available at
13  * http://www.gnu.org/licenses/lgpl-2.1.html
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * LGPL HEADER END
21  */
22 /*
23  * lustre/utils/liblustreapi_chlg.c
24  *
25  * lustreapi library for filesystem changelog
26  *
27  * Author: Henri Doreau <henri.doreau@cea.fr>
28  */
29
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <poll.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39
40 #include <lustre/lustreapi.h>
41
42
43 static int chlg_dev_path(char *path, size_t path_len, const char *device)
44 {
45         int rc;
46
47         rc = snprintf(path, path_len, "/dev/changelog-%s", device);
48         if (rc < 0)
49                 return -EIO;
50
51         if (rc >= path_len)
52                 return -EOVERFLOW;
53
54         return 0;
55 }
56
57 #define CHANGELOG_PRIV_MAGIC 0xCA8E1080
58 #define CHANGELOG_BUFFER_SZ  4096
59
60 /**
61  * Record state for efficient changelog consumption.
62  * Read chunks of CHANGELOG_BUFFER_SZ bytes.
63  */
64 struct changelog_private {
65         /* Ensure that the structure is valid and initialized */
66         int                              clp_magic;
67         /* File descriptor on the changelog character device */
68         int                              clp_fd;
69         /* Changelog delivery mode */
70         enum changelog_send_flag         clp_send_flags;
71         /* Available bytes in buffer */
72         size_t                           clp_buf_len;
73         /* Current position in buffer */
74         char                            *clp_buf_pos;
75         /* Read buffer with records read from system */
76         char                             clp_buf[0];
77 };
78
79 /**
80  * Start reading from a changelog
81  *
82  * @param priv      Opaque private control structure
83  * @param flags     Start flags (e.g. CHANGELOG_FLAG_JOBID)
84  * @param device    Report changes recorded on this MDT
85  * @param startrec  Report changes beginning with this record number
86  * (just call llapi_changelog_fini when done; don't need an endrec)
87  */
88 int llapi_changelog_start(void **priv, enum changelog_send_flag flags,
89                           const char *device, long long startrec)
90 {
91         struct changelog_private *cp;
92         static bool warned_jobid;
93         static bool warned_follow;
94         char cdev_path[PATH_MAX];
95         int rc;
96
97         rc = chlg_dev_path(cdev_path, sizeof(cdev_path), device);
98         if (rc != 0)
99                 return rc;
100
101         /* Set up the receiver control struct */
102         cp = calloc(1, sizeof(*cp) + CHANGELOG_BUFFER_SZ);
103         if (cp == NULL)
104                 return -ENOMEM;
105
106         cp->clp_magic = CHANGELOG_PRIV_MAGIC;
107         cp->clp_send_flags = flags;
108
109         cp->clp_buf_len = 0;
110         cp->clp_buf_pos = cp->clp_buf;
111
112         /* Set up the receiver */
113         cp->clp_fd = open(cdev_path, O_RDONLY);
114         if (cp->clp_fd < 0) {
115                 rc = -errno;
116                 goto out_free_cp;
117         }
118
119         if (startrec != 0) {
120                 off_t res;
121
122                 res = lseek(cp->clp_fd, startrec, SEEK_SET);
123                 if (res == (off_t)-1) {
124                         rc = -errno;
125                         goto out_close;
126                 }
127         }
128
129         *priv = cp;
130
131         /* CHANGELOG_FLAG_JOBID will eventually become mandatory. Display a
132          * warning if it's missing. */
133         if (!(flags & CHANGELOG_FLAG_JOBID) && !warned_jobid) {
134                 llapi_err_noerrno(LLAPI_MSG_WARN, "warning: %s() called "
135                                   "without CHANGELOG_FLAG_JOBID", __func__);
136                 warned_jobid = true;
137         }
138
139         /* Behavior expected by CHANGELOG_FLAG_FOLLOW is not implemented, warn
140          * the user and ignore it. */
141         if (flags & CHANGELOG_FLAG_FOLLOW && !warned_follow) {
142                 llapi_err_noerrno(LLAPI_MSG_WARN, "warning: %s() called with "
143                                   "CHANGELOG_FLAG_FOLLOW (ignored)", __func__);
144                 warned_follow = true;
145         }
146
147         return 0;
148
149 out_close:
150         close(cp->clp_fd);
151 out_free_cp:
152         free(cp);
153         return rc;
154 }
155
156 /** Finish reading from a changelog */
157 int llapi_changelog_fini(void **priv)
158 {
159         struct changelog_private *cp = *priv;
160
161         if (!cp || (cp->clp_magic != CHANGELOG_PRIV_MAGIC))
162                 return -EINVAL;
163
164         close(cp->clp_fd);
165         free(cp);
166         *priv = NULL;
167         return 0;
168 }
169
170 static ssize_t chlg_read_bulk(struct changelog_private *cp)
171 {
172         ssize_t rd_bytes;
173
174         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
175                 return -EINVAL;
176
177         rd_bytes = read(cp->clp_fd, cp->clp_buf, CHANGELOG_BUFFER_SZ);
178         if (rd_bytes < 0)
179                 return -errno;
180
181         cp->clp_buf_pos = cp->clp_buf;
182         cp->clp_buf_len = rd_bytes;
183
184         return rd_bytes;
185 }
186
187 /**
188  * Returns a file descriptor to poll on.
189  *
190  * \@param[in]  priv  Opaque changelog reader structure.
191  * @return valid file descriptor on success, negated errno code on failure.
192  */
193 int llapi_changelog_get_fd(void *priv)
194 {
195         struct changelog_private *cp = priv;
196
197         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
198                 return -EINVAL;
199
200         return cp->clp_fd;
201 }
202
203 /** Read the next changelog entry
204  * @param priv Opaque private control structure
205  * @param rech Changelog record handle; record will be allocated here
206  * @return 0 valid message received; rec is set
207  *       <0 error code
208  *       1 EOF
209  */
210 #define DEFAULT_RECORD_FMT      (CLF_VERSION | CLF_RENAME)
211 int llapi_changelog_recv(void *priv, struct changelog_rec **rech)
212 {
213         struct changelog_private *cp = priv;
214         enum changelog_rec_flags rec_fmt = DEFAULT_RECORD_FMT;
215         struct changelog_rec *tmp;
216         int rc = 0;
217
218         if (!cp || (cp->clp_magic != CHANGELOG_PRIV_MAGIC))
219                 return -EINVAL;
220
221         if (rech == NULL)
222                 return -EINVAL;
223
224         *rech = malloc(CR_MAXSIZE);
225         if (*rech == NULL)
226                 return -ENOMEM;
227
228         if (cp->clp_send_flags & CHANGELOG_FLAG_JOBID)
229                 rec_fmt |= CLF_JOBID;
230
231         if (cp->clp_buf + cp->clp_buf_len <= cp->clp_buf_pos) {
232                 ssize_t refresh;
233
234                 refresh = chlg_read_bulk(cp);
235                 if (refresh == 0) {
236                         /* EOF, CHANGELOG_FLAG_FOLLOW ignored for now LU-7659 */
237                         rc = 1;
238                         goto out_free;
239                 } else if (refresh < 0) {
240                         rc = refresh;
241                         goto out_free;
242                 }
243         }
244
245         /* TODO check changelog_rec_size */
246         tmp = (struct changelog_rec *)cp->clp_buf_pos;
247
248         memcpy(*rech, cp->clp_buf_pos,
249                changelog_rec_size(tmp) + tmp->cr_namelen);
250
251         cp->clp_buf_pos += changelog_rec_size(tmp) + tmp->cr_namelen;
252         changelog_remap_rec(*rech, rec_fmt);
253
254         return 0;
255
256 out_free:
257         free(*rech);
258         *rech = NULL;
259         return rc;
260 }
261
262 /** Release the changelog record when done with it. */
263 int llapi_changelog_free(struct changelog_rec **rech)
264 {
265         free(*rech);
266         *rech = NULL;
267         return 0;
268 }
269
270 int llapi_changelog_clear(const char *mdtname, const char *idstr,
271                           long long endrec)
272 {
273         char dev_path[PATH_MAX];
274         char cmd[64];
275         size_t cmd_len = sizeof(cmd);
276         int fd;
277         int rc;
278
279         if (endrec < 0) {
280                 llapi_err_noerrno(LLAPI_MSG_ERROR,
281                                   "can't purge negative records\n");
282                 return -EINVAL;
283         }
284
285         chlg_dev_path(dev_path, sizeof(dev_path), mdtname);
286
287         rc = snprintf(cmd, cmd_len, "clear:%s:%lld", idstr, endrec);
288         if (rc >= sizeof(cmd))
289                 return -EINVAL;
290
291         cmd_len = rc + 1;
292
293         fd = open(dev_path, O_WRONLY);
294         if (fd < 0) {
295                 rc = -errno;
296                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", dev_path);
297                 return rc;
298         }
299
300         rc = write(fd, cmd, cmd_len);
301         if (rc < 0) {
302                 rc = -errno;
303                 llapi_error(LLAPI_MSG_ERROR, rc,
304                             "cannot purge records for '%s'", idstr);
305                 goto out_close;
306         }
307
308         rc = 0;
309
310 out_close:
311         close(fd);
312         return rc;
313 }