File: helpers.c

package info (click to toggle)
libnbd 1.24.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 10,956 kB
  • sloc: ansic: 55,063; ml: 12,326; sh: 8,817; python: 4,757; makefile: 3,036; perl: 165; cpp: 24
file content (215 lines) | stat: -rw-r--r-- 5,328 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* NBD client library in userspace
 * Copyright Red Hat
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Miscellaneous helper functions used in the OCaml bindings. */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <assert.h>

#include <caml/alloc.h>
#include <caml/callback.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>
#include <caml/printexc.h>
#include <caml/socketaddr.h>
#include <caml/unixsupport.h>

#include <libnbd.h>

#include "nbd-c.h"

/* NB: @@noalloc function */
value
nbd_internal_code_of_unix_error (value error)
{
  return Val_int (code_of_unix_error (error));
}

void
nbd_internal_ocaml_raise_error (void)
{
  CAMLparam0 ();
  CAMLlocal3 (sv, errv, ev);
  value v[2];
  const char *msg;
  int err;

  msg = nbd_get_error ();
  err = nbd_get_errno ();

  if (msg)
    sv = caml_copy_string (msg);
  else
    sv = caml_copy_string ("no error message available");

  /* Convert raw C errno to OCaml Unix.error, if available. */
  if (err == 0) {
    errv = Val_int (0); /* None */
  }
  else {
    ev = unix_error_of_code (err);
    errv = caml_alloc (1, 0);   /* Some ev */
    Field (errv, 0) = ev;
  }

  v[0] = sv;
  v[1] = errv;
  caml_raise_with_args (*caml_named_value ("nbd_internal_ocaml_error"),
                        2, v);
  CAMLnoreturn;
}

void
nbd_internal_ocaml_raise_closed (const char *func)
{
  CAMLparam0 ();
  CAMLlocal1 (sv);

  sv = caml_copy_string (func);
  caml_raise_with_arg (*caml_named_value ("nbd_internal_ocaml_closed"), sv);
  CAMLnoreturn;
}

/* The caller should free the array, but NOT the strings, since the
 * strings point to OCaml strings.
 */
const char **
nbd_internal_ocaml_string_list (value ssv)
{
  CAMLparam1 (ssv);
  CAMLlocal1 (sv);
  size_t i, len;
  const char **r;

  sv = ssv;
  for (len = 0; sv != Val_emptylist; sv = Field (sv, 1))
    len++;

  r = malloc (sizeof (const char *) * (len+1));
  if (r == NULL) caml_raise_out_of_memory ();

  sv = ssv;
  for (i = 0; sv != Val_emptylist; sv = Field (sv, 1), i++)
    r[i] = String_val (Field (sv, 0));

  r[len] = NULL;
  CAMLreturnT (const char **, r);
}

value
nbd_internal_ocaml_alloc_i64_from_u32_array (uint32_t *a, size_t len)
{
  CAMLparam0 ();
  CAMLlocal2 (v, rv);
  size_t i;

  rv = caml_alloc (len, 0);
  for (i = 0; i < len; ++i) {
    v = caml_copy_int64 (a[i]);
    Store_field (rv, i, v);
  }

  CAMLreturn (rv);
}

value
nbd_internal_ocaml_alloc_extent64_array (nbd_extent *a, size_t len)
{
  CAMLparam0 ();
  CAMLlocal3 (s, v, rv);
  size_t i;

  rv = caml_alloc (len, 0);
  for (i = 0; i < len; ++i) {
    s = caml_alloc (2, 0);
    assert (a[i].length <= INT64_MAX);  /* API ensures size fits in 63 bits */
    v = caml_copy_int64 (a[i].length);
    Store_field (s, 0, v);
    v = caml_copy_int64 (a[i].flags);
    Store_field (s, 1, v);
    Store_field (rv, i, s);
  }

  CAMLreturn (rv);
}

/* Convert a Unix.sockaddr to a C struct sockaddr. */
void
nbd_internal_unix_sockaddr_to_sa (value sockaddrv,
                                  struct sockaddr_storage *ss,
                                  socklen_t *len)
{
  CAMLparam1 (sockaddrv);
  union sock_addr_union saddr_u;
  socklen_param_type sl; /* this is really an int or socklen_t */

  memset (ss, 0, sizeof *ss);

#ifdef HAVE_CAML_UNIX_GET_SOCKADDR
  caml_unix_get_sockaddr (sockaddrv, &saddr_u, &sl);
#else
  /* OCaml <= 4.14 exports this unnamespaced symbol. */
  get_sockaddr (sockaddrv, &saddr_u, &sl);
#endif
  assert (sl <= sizeof *ss);
  memcpy (ss, &saddr_u, sl);
  *len = sl;

  CAMLreturn0;
}

/* Common code when an exception is raised in an OCaml callback.
 *
 * We handle Assert_failure specially by abort()-ing.  Other
 * exceptions are printed to make sure we don't lose information.
 */
void
nbd_internal_ocaml_exception_in_wrapper (const char *cbname, value rv)
{
  CAMLparam1 (rv);
  CAMLlocal1 (exn);
  const char *exn_name;
  char *s;

  exn = Extract_exception (rv);
  s = caml_format_exception (exn);
  fprintf (stderr,
           "libnbd: %s: uncaught OCaml exception: %s\n", cbname, s);
  free (s);

  /* For how we're getting the exception name, see:
   * https://github.com/libguestfs/libguestfs/blob/5d94be2583d557cfc7f8a8cfee7988abfa45a3f8/daemon/daemon-c.c#L40
   */
  if (Tag_val (Field (exn, 0)) == String_tag)
    exn_name = String_val (Field (exn, 0));
  else
    exn_name = String_val (Field (Field (exn, 0), 0));

  /* If the exception is fatal (like Assert_failure) then abort. */
  if (exn_name && strcmp (exn_name, "Assert_failure") == 0)
    abort ();

  CAMLreturn0;
}