IDZEBRA  2.2.7
mfile.c
Go to the documentation of this file.
1 /* This file is part of the Zebra server.
2  Copyright (C) Index Data
3 
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8 
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 
18 */
19 
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #ifdef WIN32
26 #include <io.h>
27 #endif
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <direntz.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <assert.h>
36 #include <errno.h>
37 
38 #include <zebra-lock.h>
39 #include <idzebra/util.h>
40 #include <yaz/yaz-util.h>
41 #include <yaz/snprintf.h>
42 #include "mfile.h"
43 
44 static int scan_areadef(MFile_area ma, const char *ad, const char *base)
45 {
46  /*
47  * If no definition is given, use current directory, unlimited.
48  */
49  char dirname[FILENAME_MAX+1];
50  mf_dir **dp = &ma->dirs, *dir = *dp;
51 
52  if (!ad)
53  ad = ".:-1b";
54  for (;;)
55  {
56  const char *ad0 = ad;
57  int i = 0, fact = 1, multi;
58  mfile_off_t size = 0;
59 
60  while (*ad == ' ' || *ad == '\t')
61  ad++;
62  if (!*ad)
63  break;
64  if (!yaz_is_abspath(ad) && base)
65  {
66  strcpy(dirname, base);
67  i = strlen(dirname);
68  dirname[i++] = '/';
69  }
70  while (*ad)
71  {
72  if (*ad == ':' && strchr("+-0123456789", ad[1]))
73  break;
74  if (i < FILENAME_MAX)
75  dirname[i++] = *ad;
76  ad++;
77  }
78  dirname[i] = '\0';
79  if (*ad++ != ':')
80  {
81  yaz_log(YLOG_WARN, "Missing colon after path: %s", ad0);
82  return -1;
83  }
84  if (i == 0)
85  {
86  yaz_log(YLOG_WARN, "Empty path: %s", ad0);
87  return -1;
88  }
89  while (*ad == ' ' || *ad == '\t')
90  ad++;
91  if (*ad == '-')
92  {
93  fact = -1;
94  ad++;
95  }
96  else if (*ad == '+')
97  ad++;
98  size = 0;
99  if (*ad < '0' || *ad > '9')
100  {
101  yaz_log(YLOG_FATAL, "Missing size after path: %s", ad0);
102  return -1;
103  }
104  size = 0;
105  while (*ad >= '0' && *ad <= '9')
106  size = size*10 + (*ad++ - '0');
107  switch (*ad)
108  {
109  case 'B': case 'b': multi = 1; break;
110  case 'K': case 'k': multi = 1024; break;
111  case 'M': case 'm': multi = 1048576; break;
112  case 'G': case 'g': multi = 1073741824; break;
113  case '\0':
114  yaz_log(YLOG_FATAL, "Missing unit: %s", ad0);
115  return -1;
116  default:
117  yaz_log(YLOG_FATAL, "Illegal unit: %c in %s", *ad, ad0);
118  return -1;
119  }
120  ad++;
121  *dp = dir = (mf_dir *) xmalloc(sizeof(mf_dir));
122  dir->next = 0;
123  strcpy(dir->name, dirname);
124  dir->max_bytes = dir->avail_bytes = fact * size * multi;
125  dp = &dir->next;
126  }
127  return 0;
128 }
129 
139 static zint file_position(MFile mf, zint pos, int offset)
140 {
141  zint off = 0, ps;
142  int c = mf->cur_file;
143 
144  if ((c > 0 && pos <= mf->files[c-1].top) ||
145  (c < mf->no_files -1 && pos > mf->files[c].top))
146  {
147  c = 0;
148  while (c + 1 < mf->no_files && mf->files[c].top < pos)
149  {
150  off += mf->files[c].blocks;
151  c++;
152  }
153  assert(c < mf->no_files);
154  }
155  else
156  off = c ? (mf->files[c-1].top + 1) : 0;
157  if (mf->files[c].fd < 0)
158  {
159  if ((mf->files[c].fd = open(mf->files[c].path,
160  mf->wr ?
161  (O_BINARY|O_RDWR|O_CREAT) :
162  (O_BINARY|O_RDONLY), 0666)) < 0)
163  {
164  if (!mf->wr && errno == ENOENT && off == 0)
165  {
166  /* we can't open it for reading. But not really an error */
167  return -2;
168  }
169  yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to open %s", mf->files[c].path);
170  return -1;
171  }
172  }
173  ps = pos - off;
174  if (mfile_seek(mf->files[c].fd, ps *(mfile_off_t) mf->blocksize + offset,
175  SEEK_SET) < 0)
176  {
177  yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to seek in %s", mf->files[c].path);
178  yaz_log(YLOG_WARN, "pos=" ZINT_FORMAT " off=" ZINT_FORMAT " blocksize=%d offset=%d",
179  pos, off, mf->blocksize, offset);
180  return -1;
181  }
182  mf->cur_file = c;
183  return ps;
184 }
185 
186 static int cmp_part_file(const void *p1, const void *p2)
187 {
188  zint d = ((part_file *)p1)->number - ((part_file *)p2)->number;
189  if (d > 0)
190  return 1;
191  if (d < 0)
192  return -1;
193  return 0;
194 }
195 
196 MFile_area mf_init(const char *name, const char *spec, const char *base,
197  int only_shadow_files)
198 {
199  MFile_area ma = (MFile_area) xmalloc(sizeof(*ma));
200  mf_dir *dirp;
201  meta_file *meta_f;
202  part_file *part_f = 0;
203  DIR *dd;
204  struct dirent *dent;
205  int fd, number;
206  char metaname[FILENAME_MAX+1], tmpnam[FILENAME_MAX+1];
207 
208  yaz_log(YLOG_DEBUG, "mf_init(%s)", name);
209  strcpy(ma->name, name);
210  ma->mfiles = 0;
211  ma->dirs = 0;
212  if (scan_areadef(ma, spec, base) < 0)
213  {
214  yaz_log(YLOG_WARN, "Failed to access description of '%s'", name);
215  mf_destroy(ma);
216  return 0;
217  }
218  /* look at each directory */
219  for (dirp = ma->dirs; dirp; dirp = dirp->next)
220  {
221  if (!(dd = opendir(dirp->name)))
222  {
223  yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to open directory %s",
224  dirp->name);
225  mf_destroy(ma);
226  return 0;
227  }
228  /* look at each file */
229  while ((dent = readdir(dd)))
230  {
231  int len = strlen(dent->d_name);
232  const char *cp = strrchr(dent->d_name, '-');
233  if (strchr(".-", *dent->d_name))
234  continue;
235  if (len < 5 || !cp || strcmp(dent->d_name + len - 3, ".mf"))
236  continue;
237  number = atoi(cp+1);
238  memcpy(metaname, dent->d_name, cp - dent->d_name);
239  metaname[ cp - dent->d_name] = '\0';
240 
241  /* only files such as file-i-0.mf and file-i-b-0.mf, bug #739 */
242  if (only_shadow_files && cp[-2] != '-')
243  continue;
244  if (!only_shadow_files && cp[-2] == '-')
245  continue;
246  for (meta_f = ma->mfiles; meta_f; meta_f = meta_f->next)
247  {
248  /* known metafile */
249  if (!strcmp(meta_f->name, metaname))
250  {
251  part_f = &meta_f->files[meta_f->no_files++];
252  break;
253  }
254  }
255  /* new metafile */
256  if (!meta_f)
257  {
258  meta_f = (meta_file *) xmalloc(sizeof(*meta_f));
259  zebra_mutex_init(&meta_f->mutex);
260  meta_f->ma = ma;
261  meta_f->next = ma->mfiles;
262  meta_f->open = 0;
263  meta_f->cur_file = -1;
264  ma->mfiles = meta_f;
265  strcpy(meta_f->name, metaname);
266  part_f = &meta_f->files[0];
267  meta_f->no_files = 1;
268  }
269  part_f->number = number;
270  part_f->dir = dirp;
271  part_f->fd = -1;
272  yaz_snprintf(tmpnam, FILENAME_MAX,
273  "%s/%s", dirp->name, dent->d_name);
274  part_f->path = xstrdup(tmpnam);
275  /* get size */
276  if ((fd = open(part_f->path, O_BINARY|O_RDONLY)) < 0)
277  {
278  yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to access %s",
279  dent->d_name);
280  closedir(dd);
281  mf_destroy(ma);
282  return 0;
283  }
284  if ((part_f->bytes = mfile_seek(fd, 0, SEEK_END)) < 0)
285  {
286  yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to seek in %s",
287  dent->d_name);
288  close(fd);
289  closedir(dd);
290  mf_destroy(ma);
291  return 0;
292  }
293  close(fd);
294  if (dirp->max_bytes >= 0)
295  dirp->avail_bytes -= part_f->bytes;
296  }
297  closedir(dd);
298  }
299  for (meta_f = ma->mfiles; meta_f; meta_f = meta_f->next)
300  {
301  yaz_log(YLOG_DEBUG, "mf_init: %s consists of %d part(s)", meta_f->name,
302  meta_f->no_files);
303  qsort(meta_f->files, meta_f->no_files, sizeof(part_file),
304  cmp_part_file);
305  }
306  return ma;
307 }
308 
310 {
311  mf_dir *dp;
312 
313  if (!ma)
314  return;
315  dp = ma->dirs;
316  while (dp)
317  {
318  mf_dir *d = dp;
319  dp = dp->next;
320  xfree(d);
321  }
322  mf_reset(ma, 0);
323  xfree(ma);
324 }
325 
326 void mf_reset(MFile_area ma, int unlink_flag)
327 {
328  meta_file *meta_f;
329 
330  if (!ma)
331  return;
332  meta_f = ma->mfiles;
333  while (meta_f)
334  {
335  int i;
336  meta_file *m = meta_f;
337 
338  meta_f = meta_f->next;
339 
340  assert(!m->open);
341  for (i = 0; i<m->no_files; i++)
342  {
343  if (unlink_flag)
344  unlink(m->files[i].path);
345  xfree(m->files[i].path);
346  }
348  xfree(m);
349  }
350  ma->mfiles = 0;
351 }
352 
353 MFile mf_open(MFile_area ma, const char *name, int block_size, int wflag)
354 {
355  meta_file *mnew;
356  int i;
357  char tmp[FILENAME_MAX+1];
358  mf_dir *dp;
359 
360  yaz_log(YLOG_DEBUG, "mf_open(%s bs=%d, %s)", name, block_size,
361  wflag ? "RW" : "RDONLY");
362  assert(ma);
363  for (mnew = ma->mfiles; mnew; mnew = mnew->next)
364  if (!strcmp(name, mnew->name))
365  {
366  if (mnew->open)
367  {
368  yaz_log(YLOG_WARN, "metafile %s already open", name);
369  return 0;
370  }
371  break;
372  }
373  if (!mnew)
374  {
375  mnew = (meta_file *) xmalloc(sizeof(*mnew));
376  strcpy(mnew->name, name);
377  /* allocate one, empty file */
378  zebra_mutex_init(&mnew->mutex);
379  mnew->no_files = 1;
380  mnew->files[0].bytes = 0;
381  mnew->files[0].blocks = 0;
382  mnew->files[0].top = -1;
383  mnew->files[0].number = 0;
384  mnew->files[0].fd = -1;
385  mnew->min_bytes_creat = MF_MIN_BLOCKS_CREAT * block_size;
386  for (dp = ma->dirs; dp && dp->max_bytes >= 0 && dp->avail_bytes <
387  mnew->min_bytes_creat; dp = dp->next);
388  if (!dp)
389  {
390  yaz_log(YLOG_FATAL, "Insufficient space for file %s", name);
391  xfree(mnew);
392  return 0;
393  }
394  mnew->files[0].dir = dp;
395  yaz_snprintf(tmp, FILENAME_MAX, "%s/%s-%d.mf", dp->name, mnew->name, 0);
396  mnew->files[0].path = xstrdup(tmp);
397  mnew->ma = ma;
398  mnew->next = ma->mfiles;
399  ma->mfiles = mnew;
400  }
401  else
402  {
403  for (i = 0; i < mnew->no_files; i++)
404  {
405  if (mnew->files[i].bytes % block_size)
406  mnew->files[i].bytes += block_size - mnew->files[i].bytes %
407  block_size;
408  mnew->files[i].blocks = (int) (mnew->files[i].bytes / block_size);
409  }
410  assert(!mnew->open);
411  }
412  mnew->blocksize = block_size;
413  mnew->min_bytes_creat = MF_MIN_BLOCKS_CREAT * block_size;
414  mnew->wr=wflag;
415  mnew->cur_file = 0;
416  mnew->open = 1;
417 
418  for (i = 0; i < mnew->no_files; i++)
419  {
420  mnew->files[i].blocks = (int)(mnew->files[i].bytes / mnew->blocksize);
421  if (i == mnew->no_files - 1)
422  mnew->files[i].top = -1;
423  else
424  mnew->files[i].top =
425  i ? (mnew->files[i-1].top + mnew->files[i].blocks)
426  : (mnew->files[i].blocks - 1);
427  }
428  return mnew;
429 }
430 
432 {
433  int i;
434 
435  yaz_log(YLOG_DEBUG, "mf_close(%s)", mf->name);
436  assert(mf->open);
437  for (i = 0; i < mf->no_files; i++)
438  {
439  if (mf->files[i].fd >= 0)
440  {
441 #ifndef WIN32
442  if (mf->wr)
443  fsync(mf->files[i].fd);
444 #endif
445  close(mf->files[i].fd);
446  mf->files[i].fd = -1;
447  }
448  }
449  mf->open = 0;
450  return 0;
451 }
452 
453 int mf_read(MFile mf, zint no, int offset, int nbytes, void *buf)
454 {
455  zint rd;
456  int toread;
457 
458  zebra_mutex_lock(&mf->mutex);
459  if ((rd = file_position(mf, no, offset)) < 0)
460  {
461  if (rd == -2)
462  {
464  return 0;
465  }
466  else
467  {
468  yaz_log(YLOG_FATAL, "mf_read2 %s internal error", mf->name);
469  return -1;
470  }
471  }
472  toread = nbytes ? nbytes : mf->blocksize;
473  if ((rd = read(mf->files[mf->cur_file].fd, buf, toread)) < 0)
474  {
475  yaz_log(YLOG_FATAL|YLOG_ERRNO, "mf_read2: Read failed (%s)",
476  mf->files[mf->cur_file].path);
477  return -1;
478  }
480  if (rd < toread)
481  return 0;
482  else
483  return 1;
484 }
485 
486 int mf_write(MFile mf, zint no, int offset, int nbytes, const void *buf)
487 {
488  int ret = 0;
489  zint ps;
490  zint nblocks;
491  int towrite;
492  mf_dir *dp;
493  char tmp[FILENAME_MAX+1];
494  unsigned char dummych = '\xff';
495 
496  zebra_mutex_lock(&mf->mutex);
497  if ((ps = file_position(mf, no, offset)) < 0)
498  {
499  yaz_log(YLOG_FATAL, "mf_write: %s error (1)", mf->name);
500  ret = -1;
501  goto out;
502  }
503  /* file needs to grow */
504  while (ps >= mf->files[mf->cur_file].blocks)
505  {
506  mfile_off_t needed = (ps - mf->files[mf->cur_file].blocks + 1) *
507  mf->blocksize;
508  /* file overflow - allocate new file */
509  if (mf->files[mf->cur_file].dir->max_bytes >= 0 &&
510  needed > mf->files[mf->cur_file].dir->avail_bytes)
511  {
512  /* cap off file? */
513  if ((nblocks = (int) (mf->files[mf->cur_file].dir->avail_bytes /
514  mf->blocksize)) > 0)
515  {
516  yaz_log(YLOG_DEBUG, "Capping off file %s at pos " ZINT_FORMAT,
517  mf->files[mf->cur_file].path, nblocks);
518  if ((ps = file_position(mf,
519  (mf->cur_file ? mf->files[mf->cur_file-1].top : 0) +
520  mf->files[mf->cur_file].blocks + nblocks - 1, 0)) < 0)
521  {
522  yaz_log(YLOG_FATAL, "mf_write: %s error (2)",
523  mf->name);
524  ret = -1;
525  goto out;
526  }
527  yaz_log(YLOG_DEBUG, "ps = " ZINT_FORMAT, ps);
528  if (write(mf->files[mf->cur_file].fd, &dummych, 1) < 1)
529  {
530  yaz_log(YLOG_ERRNO|YLOG_FATAL, "mf_write: %s error (3)",
531  mf->name);
532  ret = -1;
533  goto out;
534  }
535  mf->files[mf->cur_file].blocks += nblocks;
536  mf->files[mf->cur_file].bytes += nblocks * mf->blocksize;
537  mf->files[mf->cur_file].dir->avail_bytes -= nblocks *
538  mf->blocksize;
539  }
540  /* get other bit */
541  yaz_log(YLOG_DEBUG, "Creating new file.");
542  for (dp = mf->ma->dirs; dp && dp->max_bytes >= 0 &&
543  dp->avail_bytes < needed; dp = dp->next);
544  if (!dp)
545  {
546  yaz_log(YLOG_FATAL, "mf_write: %s error (4) no more space",
547  mf->name);
548  for (dp = mf->ma->dirs; dp ; dp = dp->next) {
549  yaz_log(YLOG_FATAL,"%s: max=" ZINT_FORMAT
550  " used=" ZINT_FORMAT " available=" ZINT_FORMAT,
551  dp->name, (zint)dp->max_bytes,
552  (zint)(dp->max_bytes - dp->avail_bytes), (zint)dp->avail_bytes );
553  }
554  yaz_log(YLOG_FATAL,"Adjust the limits in your zebra.cfg");
555  ret = -1;
556  goto out;
557  }
558  mf->files[mf->cur_file].top = (mf->cur_file ?
559  mf->files[mf->cur_file-1].top : -1) +
560  mf->files[mf->cur_file].blocks;
561  mf->files[++(mf->cur_file)].top = -1;
562  mf->files[mf->cur_file].dir = dp;
563  mf->files[mf->cur_file].number =
564  mf->files[mf->cur_file-1].number + 1;
565  mf->files[mf->cur_file].blocks = 0;
566  mf->files[mf->cur_file].bytes = 0;
567  mf->files[mf->cur_file].fd = -1;
568  yaz_snprintf(tmp, FILENAME_MAX,
569  "%s/%s-" ZINT_FORMAT ".mf", dp->name, mf->name,
570  mf->files[mf->cur_file].number);
571  mf->files[mf->cur_file].path = xstrdup(tmp);
572  mf->no_files++;
573  /* open new file and position at beginning */
574  if ((ps = file_position(mf, no, offset)) < 0)
575  {
576  yaz_log(YLOG_FATAL, "mf_write: %s error (5)", mf->name);
577  ret = -1;
578  goto out;
579  }
580  }
581  else
582  {
583  nblocks = ps - mf->files[mf->cur_file].blocks + 1;
584  mf->files[mf->cur_file].blocks += nblocks;
585  mf->files[mf->cur_file].bytes += nblocks * mf->blocksize;
586  if (mf->files[mf->cur_file].dir->max_bytes >= 0)
587  mf->files[mf->cur_file].dir->avail_bytes -=
588  nblocks * mf->blocksize;
589  }
590  }
591  towrite = nbytes ? nbytes : mf->blocksize;
592  if (write(mf->files[mf->cur_file].fd, buf, towrite) < towrite)
593  {
594  yaz_log(YLOG_FATAL|YLOG_ERRNO, "Write failed for file %s part %d",
595  mf->name, mf->cur_file);
596  ret = -1;
597  }
598  out:
600  return ret;
601 }
602 
612 int mf_area_directory_stat(MFile_area ma, int no, const char **directory,
613  double *used_bytes, double *max_bytes)
614 {
615  int i;
616  mf_dir *d = ma->dirs;
617  for (i = 0; d && i<no; i++, d = d->next)
618  ;
619  if (!d)
620  return 0;
621  if (directory)
622  *directory = d->name;
623  if (max_bytes)
624  {
625  /* possible loss of data. But it's just statistics and lies */
626  *max_bytes = (double) d->max_bytes;
627  }
628  if (used_bytes)
629  {
630  /* possible loss of data. But it's just statistics and lies */
631  *used_bytes = (double) (d->max_bytes - d->avail_bytes);
632  }
633  return 1;
634 }
635 /*
636  * Local variables:
637  * c-basic-offset: 4
638  * c-file-style: "Stroustrup"
639  * indent-tabs-mode: nil
640  * End:
641  * vim: shiftwidth=4 tabstop=8 expandtab
642  */
643 
#define O_BINARY
Definition: agrep.c:46
static int cmp_part_file(const void *p1, const void *p2)
Definition: mfile.c:186
int mf_write(MFile mf, zint no, int offset, int nbytes, const void *buf)
writes block to metafile
Definition: mfile.c:486
int mf_area_directory_stat(MFile_area ma, int no, const char **directory, double *used_bytes, double *max_bytes)
metafile area statistics
Definition: mfile.c:612
static zint file_position(MFile mf, zint pos, int offset)
position within metafile (perform seek)
Definition: mfile.c:139
int mf_read(MFile mf, zint no, int offset, int nbytes, void *buf)
reads block from metafile
Definition: mfile.c:453
void mf_reset(MFile_area ma, int unlink_flag)
reset all files in a metafile area (optionally delete them as well)
Definition: mfile.c:326
void mf_destroy(MFile_area ma)
destroys metafile area handle
Definition: mfile.c:309
MFile_area mf_init(const char *name, const char *spec, const char *base, int only_shadow_files)
creates a metafile area
Definition: mfile.c:196
int mf_close(MFile mf)
closes metafile
Definition: mfile.c:431
MFile mf_open(MFile_area ma, const char *name, int block_size, int wflag)
opens metafile
Definition: mfile.c:353
static int scan_areadef(MFile_area ma, const char *ad, const char *base)
Definition: mfile.c:44
#define mfile_seek
Definition: mfile.h:37
#define MF_MIN_BLOCKS_CREAT
Definition: mfile.h:49
struct MFile_area_struct * MFile_area
Definition: mfile.h:75
off_t mfile_off_t
Definition: mfile.h:36
#define FILENAME_MAX
Definition: mfile.h:42
struct meta_file * mfiles
Definition: mfile.h:97
mf_dir * dirs
Definition: mfile.h:96
char name[FILENAME_MAX+1]
Definition: mfile.h:95
int wr
Definition: mfile.h:87
MFile_area ma
Definition: mfile.h:86
struct meta_file * next
Definition: mfile.h:90
int open
Definition: mfile.h:83
int blocksize
Definition: mfile.h:84
char name[FILENAME_MAX+1]
Definition: mfile.h:79
mfile_off_t min_bytes_creat
Definition: mfile.h:85
int no_files
Definition: mfile.h:81
part_file files[MF_MAX_PARTS]
Definition: mfile.h:80
int cur_file
Definition: mfile.h:82
Zebra_mutex mutex
Definition: mfile.h:88
Definition: mfile.h:56
mfile_off_t avail_bytes
Definition: mfile.h:59
mfile_off_t max_bytes
Definition: mfile.h:58
struct mf_dir * next
Definition: mfile.h:60
char name[FILENAME_MAX+1]
Definition: mfile.h:57
char * path
Definition: mfile.h:70
mf_dir * dir
Definition: mfile.h:69
zint number
Definition: mfile.h:65
zint top
Definition: mfile.h:66
zint blocks
Definition: mfile.h:67
mfile_off_t bytes
Definition: mfile.h:68
int fd
Definition: mfile.h:71
int fd
Definition: tstlockscope.c:38
long zint
Zebra integer.
Definition: util.h:66
#define ZINT_FORMAT
Definition: util.h:72
int zebra_mutex_unlock(Zebra_mutex *p)
Definition: zebra-lock.c:74
int zebra_mutex_init(Zebra_mutex *p)
Definition: zebra-lock.c:31
int zebra_mutex_lock(Zebra_mutex *p)
Definition: zebra-lock.c:59
int zebra_mutex_destroy(Zebra_mutex *p)
Definition: zebra-lock.c:43