Coverage Report

Created: 2024-05-20 06:35

/src/clib/src/common/clib-package.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// clib-package.c
3
//
4
// Copyright (c) 2014 Stephen Mathieson
5
// Copyright (c) 2014-2020 clib authors
6
// MIT license
7
//
8
9
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
10
#include <unistd.h>
11
#endif
12
13
#include "asprintf/asprintf.h"
14
#include "clib-cache.h"
15
#include "clib-package.h"
16
#include "clib-settings.h"
17
#include "debug/debug.h"
18
#include "fs/fs.h"
19
#include "hash/hash.h"
20
#include "http-get/http-get.h"
21
#include "logger/logger.h"
22
#include "mkdirp/mkdirp.h"
23
#include "parse-repo/parse-repo.h"
24
#include "parson/parson.h"
25
#include "path-join/path-join.h"
26
#include "rimraf/rimraf.h"
27
#include "strdup/strdup.h"
28
#include "substr/substr.h"
29
#include "tempdir/tempdir.h"
30
#include <curl/curl.h>
31
#include <libgen.h>
32
#include <limits.h>
33
#include <stdarg.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
38
#ifdef HAVE_PTHREADS
39
#include <pthread.h>
40
#endif
41
42
#ifndef DEFAULT_REPO_VERSION
43
0
#define DEFAULT_REPO_VERSION "master"
44
#endif
45
46
#ifndef DEFAULT_REPO_OWNER
47
4
#define DEFAULT_REPO_OWNER "clibs"
48
#endif
49
50
0
#define GITHUB_CONTENT_URL "https://raw.githubusercontent.com/"
51
0
#define GITHUB_CONTENT_URL_WITH_TOKEN "https://%s@raw.githubusercontent.com/"
52
53
#if defined(_WIN32) || defined(WIN32) || defined(__MINGW32__) ||               \
54
    defined(__MINGW64__)
55
#define setenv(k, v, _) _putenv_s(k, v)
56
#define realpath(a, b) _fullpath(a, b, strlen(a))
57
#endif
58
59
static hash_t *visited_packages = 0;
60
61
#ifdef HAVE_PTHREADS
62
typedef struct fetch_package_file_thread_data fetch_package_file_thread_data_t;
63
struct fetch_package_file_thread_data {
64
  clib_package_t *pkg;
65
  const char *dir;
66
  char *file;
67
  int verbose;
68
  pthread_t thread;
69
  pthread_attr_t attr;
70
  void *data;
71
};
72
73
typedef struct clib_package_lock clib_package_lock_t;
74
struct clib_package_lock {
75
  pthread_mutex_t mutex;
76
};
77
78
static clib_package_lock_t lock = {PTHREAD_MUTEX_INITIALIZER};
79
80
#endif
81
82
CURLSH *clib_package_curl_share;
83
debug_t _debugger;
84
85
#define _debug(...)                                                            \
86
23
  ({                                                                           \
87
23
    if (!(_debugger.name))                                                     \
88
23
      debug_init(&_debugger, "clib-package");                                  \
89
23
    debug(&_debugger, __VA_ARGS__);                                            \
90
23
  })
91
92
#define E_FORMAT(...)                                                          \
93
0
  ({                                                                           \
94
0
    rc = asprintf(__VA_ARGS__);                                                \
95
0
    if (-1 == rc)                                                              \
96
0
      goto cleanup;                                                            \
97
0
  });
98
99
static clib_package_opts_t opts = {
100
#ifdef HAVE_PTHREADS
101
    .concurrency = MAX_THREADS,
102
#endif
103
    .skip_cache = 1,
104
    .prefix = 0,
105
    .global = 0,
106
    .force = 0,
107
    .token = 0,
108
};
109
110
/**
111
 * Pre-declare prototypes.
112
 */
113
114
static inline char *json_object_get_string_safe(JSON_Object *, const char *);
115
116
static inline char *json_array_get_string_safe(JSON_Array *, int);
117
118
static inline char *clib_package_file_url(const char *, const char *);
119
120
static inline char *clib_package_slug(const char *, const char *, const char *);
121
122
static inline char *clib_package_repo(const char *, const char *);
123
124
static inline list_t *parse_package_deps(JSON_Object *);
125
126
static inline int install_packages(list_t *, const char *, int);
127
128
0
void clib_package_set_opts(clib_package_opts_t o) {
129
0
  if (1 == opts.skip_cache && 0 == o.skip_cache) {
130
0
    opts.skip_cache = 0;
131
0
  } else if (0 == opts.skip_cache && 1 == o.skip_cache) {
132
0
    opts.skip_cache = 1;
133
0
  }
134
135
0
  if (1 == opts.global && 0 == o.global) {
136
0
    opts.global = 0;
137
0
  } else if (0 == opts.global && 1 == o.global) {
138
0
    opts.global = 1;
139
0
  }
140
141
0
  if (1 == opts.force && 0 == o.force) {
142
0
    opts.force = 0;
143
0
  } else if (0 == opts.force && 1 == o.force) {
144
0
    opts.force = 1;
145
0
  }
146
147
0
  if (0 != o.prefix) {
148
0
    if (0 == strlen(o.prefix)) {
149
0
      opts.prefix = 0;
150
0
    } else {
151
0
      opts.prefix = o.prefix;
152
0
    }
153
0
  }
154
155
0
  if (0 != o.token) {
156
0
    if (0 == strlen(o.token)) {
157
0
      opts.token = 0;
158
0
    } else {
159
0
      opts.token = o.token;
160
0
    }
161
0
  }
162
163
0
  if (o.concurrency) {
164
0
    opts.concurrency = o.concurrency;
165
0
  } else if (o.concurrency < 0) {
166
0
    opts.concurrency = 0;
167
0
  }
168
169
0
  if (opts.concurrency < 0) {
170
0
    opts.concurrency = 0;
171
0
  }
172
0
}
173
174
/**
175
 * Create a copy of the result of a `json_object_get_string`
176
 * invocation.  This allows us to `json_value_free()` the
177
 * parent `JSON_Value` without destroying the string.
178
 */
179
180
static inline char *json_object_get_string_safe(JSON_Object *obj,
181
59
                                                const char *key) {
182
59
  const char *val = json_object_get_string(obj, key);
183
59
  if (!val)
184
55
    return NULL;
185
4
  return strdup(val);
186
59
}
187
188
/**
189
 * Create a copy of the result of a `json_array_get_string`
190
 * invocation.  This allows us to `json_value_free()` the
191
 * parent `JSON_Value` without destroying the string.
192
 */
193
194
127
static inline char *json_array_get_string_safe(JSON_Array *array, int index) {
195
127
  const char *val = json_array_get_string(array, index);
196
127
  if (!val)
197
0
    return NULL;
198
127
  return strdup(val);
199
127
}
200
201
/**
202
 * Build a URL for `file` of the package belonging to `url`
203
 */
204
205
0
static inline char *clib_package_file_url(const char *url, const char *file) {
206
0
  if (!url || !file)
207
0
    return NULL;
208
209
0
  int size = strlen(url) + 1    // /
210
0
             + strlen(file) + 1 // \0
211
0
      ;
212
213
0
  char *res = malloc(size);
214
0
  if (res) {
215
0
    memset(res, 0, size);
216
0
    sprintf(res, "%s/%s", url, file);
217
0
  }
218
0
  return res;
219
0
}
220
221
/**
222
 * Build a slug
223
 */
224
225
static inline char *clib_package_slug(const char *author, const char *name,
226
0
                                      const char *version) {
227
0
  int size = strlen(author) + 1    // /
228
0
             + strlen(name) + 1    // @
229
0
             + strlen(version) + 1 // \0
230
0
      ;
231
232
0
  char *slug = malloc(size);
233
0
  if (slug) {
234
0
    memset(slug, '\0', size);
235
0
    sprintf(slug, "%s/%s@%s", author, name, version);
236
0
  }
237
0
  return slug;
238
0
}
239
240
/**
241
 * Load a local package with a manifest.
242
 */
243
244
clib_package_t *clib_package_load_from_manifest(const char *manifest,
245
12
                                                int verbose) {
246
12
  clib_package_t *pkg = NULL;
247
248
12
  if (-1 == fs_exists(manifest)) {
249
0
    logger_error("error", "Missing %s", manifest);
250
0
    return NULL;
251
0
  }
252
253
12
  logger_info("info", "reading local %s", manifest);
254
255
12
  char *json = fs_read(manifest);
256
12
  if (NULL == json)
257
0
    goto e1;
258
259
12
  pkg = clib_package_new(json, verbose);
260
261
12
e1:
262
12
  free(json);
263
264
12
  return pkg;
265
12
}
266
267
/**
268
 * Load a manifest from the current path.
269
 */
270
271
0
clib_package_t *clib_package_load_local_manifest(int verbose) {
272
0
  clib_package_t *pkg = NULL;
273
0
  int i = 0;
274
275
0
  do {
276
0
    const char *name = NULL;
277
0
    name = manifest_names[i];
278
0
    pkg = clib_package_load_from_manifest(name, verbose);
279
0
  } while (pkg == NULL && NULL != manifest_names[++i]);
280
281
0
  return pkg;
282
0
}
283
284
/**
285
 * Build a repo
286
 */
287
288
0
static inline char *clib_package_repo(const char *author, const char *name) {
289
0
  int size = strlen(author) + 1 // /
290
0
             + strlen(name) + 1 // \0
291
0
      ;
292
293
0
  char *repo = malloc(size);
294
0
  if (repo) {
295
0
    memset(repo, '\0', size);
296
0
    sprintf(repo, "%s/%s", author, name);
297
0
  }
298
0
  return repo;
299
0
}
300
301
/**
302
 * Parse the dependencies in the given `obj` into a `list_t`
303
 */
304
305
1
static inline list_t *parse_package_deps(JSON_Object *obj) {
306
1
  list_t *list = NULL;
307
308
1
  if (!obj)
309
0
    goto done;
310
1
  if (!(list = list_new()))
311
0
    goto done;
312
1
  list->free = clib_package_dependency_free;
313
314
5
  for (unsigned int i = 0; i < json_object_get_count(obj); i++) {
315
4
    const char *name = NULL;
316
4
    char *version = NULL;
317
4
    clib_package_dependency_t *dep = NULL;
318
4
    int error = 1;
319
320
4
    if (!(name = json_object_get_name(obj, i)))
321
0
      goto loop_cleanup;
322
4
    if (!(version = json_object_get_string_safe(obj, name)))
323
0
      goto loop_cleanup;
324
4
    if (!(dep = clib_package_dependency_new(name, version)))
325
0
      goto loop_cleanup;
326
327
4
    list_node_t *dep_node = list_node_new(dep);
328
    // note: if we fail to allocate the node itself,
329
    // `dep` will never be pushed on the list
330
4
    if (!dep_node) {
331
0
      clib_package_dependency_free(dep);
332
0
      goto loop_cleanup;
333
0
    }
334
335
4
    if (!(list_rpush(list, dep_node)))
336
0
      goto loop_cleanup;
337
338
4
    error = 0;
339
340
4
  loop_cleanup:
341
4
    if (version)
342
4
      free(version);
343
4
    if (error) {
344
0
      list_destroy(list);
345
0
      list = NULL;
346
0
      break;
347
0
    }
348
4
  }
349
350
1
done:
351
1
  return list;
352
1
}
353
354
0
static inline int install_packages(list_t *list, const char *dir, int verbose) {
355
0
  list_node_t *node = NULL;
356
0
  list_iterator_t *iterator = NULL;
357
0
  int rc = -1;
358
0
  list_t *freelist = NULL;
359
360
0
  if (!list || !dir)
361
0
    goto cleanup;
362
363
0
  iterator = list_iterator_new(list, LIST_HEAD);
364
0
  if (NULL == iterator)
365
0
    goto cleanup;
366
367
0
  freelist = list_new();
368
369
0
  while ((node = list_iterator_next(iterator))) {
370
0
    clib_package_dependency_t *dep = NULL;
371
0
    char *slug = NULL;
372
0
    clib_package_t *pkg = NULL;
373
0
    int error = 1;
374
375
0
    dep = (clib_package_dependency_t *)node->val;
376
0
    slug = clib_package_slug(dep->author, dep->name, dep->version);
377
0
    if (NULL == slug)
378
0
      goto loop_cleanup;
379
380
0
    pkg = clib_package_new_from_slug(slug, verbose);
381
0
    if (NULL == pkg)
382
0
      goto loop_cleanup;
383
384
0
    if (-1 == clib_package_install(pkg, dir, verbose))
385
0
      goto loop_cleanup;
386
387
0
    list_rpush(freelist, list_node_new(pkg));
388
0
    error = 0;
389
390
0
  loop_cleanup:
391
0
    if (slug)
392
0
      free(slug);
393
0
    if (error) {
394
0
      list_iterator_destroy(iterator);
395
0
      iterator = NULL;
396
0
      rc = -1;
397
0
      goto cleanup;
398
0
    }
399
0
  }
400
401
0
  rc = 0;
402
403
0
cleanup:
404
0
  if (iterator)
405
0
    list_iterator_destroy(iterator);
406
407
0
  if (freelist) {
408
0
    iterator = list_iterator_new(freelist, LIST_HEAD);
409
0
    while ((node = list_iterator_next(iterator))) {
410
0
      clib_package_t *pkg = node->val;
411
0
      if (pkg)
412
0
        clib_package_free(pkg);
413
0
    }
414
0
    list_iterator_destroy(iterator);
415
0
    list_destroy(freelist);
416
0
  }
417
0
  return rc;
418
0
}
419
420
#ifdef HAVE_PTHREADS
421
static void curl_lock_callback(CURL *handle, curl_lock_data data,
422
0
                               curl_lock_access access, void *userptr) {
423
0
  pthread_mutex_lock(&lock.mutex);
424
0
}
425
426
static void curl_unlock_callback(CURL *handle, curl_lock_data data,
427
0
                                 curl_lock_access access, void *userptr) {
428
0
  pthread_mutex_unlock(&lock.mutex);
429
0
}
430
431
0
static void init_curl_share() {
432
0
  if (0 == clib_package_curl_share) {
433
0
    pthread_mutex_lock(&lock.mutex);
434
0
    clib_package_curl_share = curl_share_init();
435
0
    curl_share_setopt(clib_package_curl_share, CURLSHOPT_SHARE,
436
0
                      CURL_LOCK_DATA_CONNECT);
437
0
    curl_share_setopt(clib_package_curl_share, CURLSHOPT_LOCKFUNC,
438
0
                      curl_lock_callback);
439
0
    curl_share_setopt(clib_package_curl_share, CURLSHOPT_UNLOCKFUNC,
440
0
                      curl_unlock_callback);
441
0
    curl_share_setopt(clib_package_curl_share, CURLOPT_NETRC,
442
0
                      CURL_NETRC_OPTIONAL);
443
0
    pthread_mutex_unlock(&lock.mutex);
444
0
  }
445
0
}
446
#endif
447
448
/**
449
 * Create a new clib package from the given `json`
450
 */
451
452
12
clib_package_t *clib_package_new(const char *json, int verbose) {
453
12
  clib_package_t *pkg = NULL;
454
12
  JSON_Value *root = NULL;
455
12
  JSON_Object *json_object = NULL;
456
12
  JSON_Array *src = NULL;
457
12
  JSON_Object *deps = NULL;
458
12
  JSON_Object *devs = NULL;
459
12
  int error = 1;
460
461
12
  if (!json) {
462
0
    if (verbose) {
463
0
      logger_error("error", "missing JSON to parse");
464
0
    }
465
0
    goto cleanup;
466
0
  }
467
468
12
  if (!(root = json_parse_string(json))) {
469
7
    if (verbose) {
470
0
      logger_error("error", "unable to parse JSON");
471
0
    }
472
7
    goto cleanup;
473
7
  }
474
475
5
  if (!(json_object = json_value_get_object(root))) {
476
0
    if (verbose) {
477
0
      logger_error("error", "invalid clib.json or package.json file");
478
0
    }
479
0
    goto cleanup;
480
0
  }
481
482
5
  if (!(pkg = malloc(sizeof(clib_package_t)))) {
483
0
    goto cleanup;
484
0
  }
485
486
5
  memset(pkg, 0, sizeof(clib_package_t));
487
488
5
  pkg->json = strdup(json);
489
5
  pkg->name = json_object_get_string_safe(json_object, "name");
490
5
  pkg->repo = json_object_get_string_safe(json_object, "repo");
491
5
  pkg->version = json_object_get_string_safe(json_object, "version");
492
5
  pkg->license = json_object_get_string_safe(json_object, "license");
493
5
  pkg->description = json_object_get_string_safe(json_object, "description");
494
5
  pkg->configure = json_object_get_string_safe(json_object, "configure");
495
5
  pkg->install = json_object_get_string_safe(json_object, "install");
496
5
  pkg->makefile = json_object_get_string_safe(json_object, "makefile");
497
5
  pkg->prefix = json_object_get_string_safe(json_object, "prefix");
498
5
  pkg->flags = json_object_get_string_safe(json_object, "flags");
499
500
5
  if (!pkg->flags) {
501
5
    pkg->flags = json_object_get_string_safe(json_object, "cflags");
502
5
  }
503
504
  // try as array
505
5
  if (!pkg->flags) {
506
5
    JSON_Array *flags = json_object_get_array(json_object, "flags");
507
508
5
    if (!flags) {
509
4
      flags = json_object_get_array(json_object, "cflags");
510
4
    }
511
512
5
    if (flags) {
513
128
      for (unsigned int i = 0; i < json_array_get_count(flags); i++) {
514
127
        char *flag = json_array_get_string_safe(flags, i);
515
127
        if (flag) {
516
127
          if (!pkg->flags) {
517
1
            pkg->flags = "";
518
1
          }
519
520
127
          if (-1 == asprintf(&pkg->flags, "%s %s", pkg->flags, flag)) {
521
0
            goto cleanup;
522
0
          }
523
524
127
          free(flag);
525
127
        }
526
127
      }
527
1
    }
528
5
  }
529
530
5
  if (!pkg->repo && pkg->author && pkg->name) {
531
0
    asprintf(&pkg->repo, "%s/%s", pkg->author, pkg->name);
532
0
    _debug("creating package: %s", pkg->repo);
533
0
  }
534
535
5
  if (!pkg->author) {
536
5
    _debug("unable to determine package author for: %s", pkg->name);
537
5
  }
538
539
  // TODO npm-style "repository" (thlorenz/gumbo-parser.c#1)
540
5
  if (pkg->repo) {
541
0
    pkg->author = parse_repo_owner(pkg->repo, DEFAULT_REPO_OWNER);
542
    // repo name may not be package name (thing.c -> thing)
543
0
    pkg->repo_name = parse_repo_name(pkg->repo);
544
5
  } else {
545
5
    if (verbose) {
546
0
      logger_warn("warning",
547
0
                  "missing repo in clib.json or package.json file for %s",
548
0
                  pkg->name);
549
0
    }
550
5
    pkg->author = NULL;
551
5
    pkg->repo_name = NULL;
552
5
  }
553
554
5
  src = json_object_get_array(json_object, "src");
555
556
5
  if (!src) {
557
5
    src = json_object_get_array(json_object, "files");
558
5
  }
559
560
5
  if (src) {
561
0
    if (!(pkg->src = list_new()))
562
0
      goto cleanup;
563
0
    pkg->src->free = free;
564
0
    for (unsigned int i = 0; i < json_array_get_count(src); i++) {
565
0
      char *file = json_array_get_string_safe(src, i);
566
0
      _debug("file: %s", file);
567
0
      if (!file)
568
0
        goto cleanup;
569
0
      if (!(list_rpush(pkg->src, list_node_new(file))))
570
0
        goto cleanup;
571
0
    }
572
5
  } else {
573
5
    _debug("no src files listed in clib.json or package.json file");
574
5
    pkg->src = NULL;
575
5
  }
576
577
5
  if ((deps = json_object_get_object(json_object, "dependencies"))) {
578
0
    if (!(pkg->dependencies = parse_package_deps(deps))) {
579
0
      goto cleanup;
580
0
    }
581
5
  } else {
582
5
    _debug("no dependencies listed in clib.json or package.json file");
583
5
    pkg->dependencies = NULL;
584
5
  }
585
586
5
  if ((devs = json_object_get_object(json_object, "development"))) {
587
1
    if (!(pkg->development = parse_package_deps(devs))) {
588
0
      goto cleanup;
589
0
    }
590
4
  } else {
591
4
    _debug(
592
4
        "no development dependencies listed in clib.json or package.json file");
593
4
    pkg->development = NULL;
594
4
  }
595
596
5
  error = 0;
597
598
12
cleanup:
599
12
  if (root)
600
5
    json_value_free(root);
601
12
  if (error && pkg) {
602
0
    clib_package_free(pkg);
603
0
    pkg = NULL;
604
0
  }
605
12
  return pkg;
606
5
}
607
608
static clib_package_t *
609
clib_package_new_from_slug_with_package_name(const char *slug, int verbose,
610
0
                                             const char *file) {
611
0
  char *author = NULL;
612
0
  char *name = NULL;
613
0
  char *version = NULL;
614
0
  char *url = NULL;
615
0
  char *json_url = NULL;
616
0
  char *repo = NULL;
617
0
  char *json = NULL;
618
0
  char *log = NULL;
619
0
  http_get_response_t *res = NULL;
620
0
  clib_package_t *pkg = NULL;
621
0
  int retries = 3;
622
623
  // parse chunks
624
0
  if (!slug)
625
0
    goto error;
626
0
  _debug("creating package: %s", slug);
627
0
  if (!(author = parse_repo_owner(slug, DEFAULT_REPO_OWNER)))
628
0
    goto error;
629
0
  if (!(name = parse_repo_name(slug)))
630
0
    goto error;
631
0
  if (!(version = parse_repo_version(slug, DEFAULT_REPO_VERSION)))
632
0
    goto error;
633
0
  if (!(url = clib_package_url(author, name, version)))
634
0
    goto error;
635
0
  if (!(json_url = clib_package_file_url(url, file)))
636
0
    goto error;
637
638
0
  _debug("author: %s", author);
639
0
  _debug("name: %s", name);
640
0
  _debug("version: %s", version);
641
642
0
#ifdef HAVE_PTHREADS
643
0
  pthread_mutex_lock(&lock.mutex);
644
0
#endif
645
  // fetch json
646
0
  if (clib_cache_has_json(author, name, version)) {
647
0
    if (opts.skip_cache) {
648
0
      clib_cache_delete_json(author, name, version);
649
0
      goto download;
650
0
    }
651
652
0
    json = clib_cache_read_json(author, name, version);
653
654
0
    if (!json) {
655
0
      goto download;
656
0
    }
657
658
0
    log = "cache";
659
0
#ifdef HAVE_PTHREADS
660
0
    pthread_mutex_unlock(&lock.mutex);
661
0
#endif
662
0
  } else {
663
0
  download:
664
0
#ifdef HAVE_PTHREADS
665
0
    pthread_mutex_unlock(&lock.mutex);
666
0
#endif
667
0
    if (retries-- <= 0) {
668
0
      goto error;
669
0
    } else {
670
0
#ifdef HAVE_PTHREADS
671
0
      init_curl_share();
672
0
      _debug("GET %s", json_url);
673
      // clean up when retrying
674
0
      http_get_free(res);
675
0
      res = http_get_shared(json_url, clib_package_curl_share);
676
#else
677
      res = http_get(json_url);
678
#endif
679
0
      json = res->data;
680
0
      _debug("status: %d", res->status);
681
0
      if (!res || !res->ok) {
682
0
        goto download;
683
0
      }
684
0
      log = "fetch";
685
0
    }
686
0
  }
687
688
0
  if (verbose) {
689
0
    logger_info(log, "%s/%s:%s", author, name, file);
690
0
  }
691
692
0
  free(json_url);
693
0
  json_url = NULL;
694
0
  free(name);
695
0
  name = NULL;
696
697
0
  if (json) {
698
    // build package
699
0
    pkg = clib_package_new(json, verbose);
700
0
  }
701
702
0
  if (!pkg)
703
0
    goto error;
704
705
  // force version number
706
0
  if (pkg->version) {
707
0
    if (0 != strcmp(version, DEFAULT_REPO_VERSION)) {
708
0
      _debug("forcing version number: %s (%s)", version, pkg->version);
709
0
      free(pkg->version);
710
0
      pkg->version = version;
711
0
    } else {
712
0
      free(version);
713
0
      version = NULL;
714
0
    }
715
0
  } else {
716
0
    pkg->version = version;
717
0
  }
718
719
  // force package author (don't know how this could fail)
720
0
  if (pkg->author) {
721
0
    if (0 != strcmp(author, pkg->author)) {
722
0
      free(pkg->author);
723
0
      pkg->author = author;
724
0
    } else {
725
0
      free(author);
726
0
      author = NULL;
727
0
    }
728
0
  } else {
729
0
    pkg->author = strdup(author);
730
0
  }
731
732
0
  if (!(repo = clib_package_repo(pkg->author, pkg->name))) {
733
0
    goto error;
734
0
  }
735
736
0
  if (pkg->repo) {
737
0
    if (0 != strcmp(repo, pkg->repo)) {
738
0
      free(url);
739
0
      if (!(url = clib_package_url_from_repo(pkg->repo, pkg->version)))
740
0
        goto error;
741
0
    }
742
0
    free(repo);
743
0
    repo = NULL;
744
0
  } else {
745
0
    pkg->repo = repo;
746
0
  }
747
748
0
  pkg->url = url;
749
750
0
#ifdef HAVE_PTHREADS
751
0
  pthread_mutex_lock(&lock.mutex);
752
0
#endif
753
  // cache json
754
0
  if (pkg && pkg->author && pkg->name && pkg->version) {
755
0
    if (-1 ==
756
0
        clib_cache_save_json(pkg->author, pkg->name, pkg->version, json)) {
757
0
      _debug("failed to cache JSON for: %s/%s@%s", pkg->author, pkg->name,
758
0
             pkg->version);
759
0
    } else {
760
0
      _debug("cached json: %s/%s@%s", pkg->author, pkg->name, pkg->version);
761
0
    }
762
0
  }
763
0
#ifdef HAVE_PTHREADS
764
0
  pthread_mutex_unlock(&lock.mutex);
765
0
#endif
766
767
0
  if (res) {
768
0
    http_get_free(res);
769
0
    json = NULL;
770
0
    res = NULL;
771
0
  } else {
772
0
    free(json);
773
0
    json = NULL;
774
0
  }
775
776
0
  return pkg;
777
778
0
error:
779
0
  if (0 == retries) {
780
0
    if (verbose && author && name && file) {
781
0
      logger_warn("warning", "unable to fetch %s/%s:%s", author, name, file);
782
0
    }
783
0
  }
784
785
0
  free(author);
786
0
  free(name);
787
0
  free(version);
788
0
  free(url);
789
0
  free(json_url);
790
0
  free(repo);
791
0
  if (!res && json)
792
0
    free(json);
793
0
  if (res)
794
0
    http_get_free(res);
795
0
  if (pkg)
796
0
    clib_package_free(pkg);
797
0
  return NULL;
798
0
}
799
800
/**
801
 * Create a package from the given repo `slug`
802
 */
803
804
0
clib_package_t *clib_package_new_from_slug(const char *slug, int verbose) {
805
0
  clib_package_t *package = NULL;
806
0
  const char *name = NULL;
807
0
  unsigned int i = 0;
808
809
0
  do {
810
0
    name = manifest_names[i];
811
0
    package = clib_package_new_from_slug_with_package_name(slug, verbose, name);
812
0
    if (NULL != package) {
813
0
      package->filename = (char *)name;
814
0
    }
815
0
  } while (NULL != manifest_names[++i] && NULL == package);
816
817
0
  return package;
818
0
}
819
820
/**
821
 * Get a slug for the package `author/name@version`
822
 */
823
824
char *clib_package_url(const char *author, const char *name,
825
0
                       const char *version) {
826
0
  if (!author || !name || !version)
827
0
    return NULL;
828
0
  int size = strlen(GITHUB_CONTENT_URL) + strlen(author) + 1 // /
829
0
             + strlen(name) + 1                              // /
830
0
             + strlen(version) + 1                           // \0
831
0
      ;
832
833
0
  if (0 != opts.token) {
834
0
    size += strlen(opts.token);
835
0
    size += 1; // @
836
0
  }
837
838
0
  char *slug = malloc(size);
839
0
  if (slug) {
840
0
    memset(slug, '\0', size);
841
0
    if (0 != opts.token) {
842
0
      sprintf(slug, GITHUB_CONTENT_URL_WITH_TOKEN "%s/%s/%s", opts.token,
843
0
              author, name, version);
844
0
    } else {
845
0
      sprintf(slug, GITHUB_CONTENT_URL "%s/%s/%s", author, name, version);
846
0
    }
847
0
  }
848
849
0
  return slug;
850
0
}
851
852
0
char *clib_package_url_from_repo(const char *repo, const char *version) {
853
0
  if (!repo || !version)
854
0
    return NULL;
855
0
  int size = strlen(GITHUB_CONTENT_URL) + strlen(repo) + 1 // /
856
0
             + strlen(version) + 1                         // \0
857
0
      ;
858
859
0
  if (0 != opts.token) {
860
0
    size += strlen(opts.token);
861
0
    size += 1; // @
862
0
  }
863
864
0
  char *slug = malloc(size);
865
0
  if (slug) {
866
0
    memset(slug, '\0', size);
867
0
    if (0 != opts.token) {
868
0
      sprintf(slug, GITHUB_CONTENT_URL_WITH_TOKEN "%s/%s", opts.token, repo,
869
0
              version);
870
0
    } else {
871
0
      sprintf(slug, GITHUB_CONTENT_URL "%s/%s", repo, version);
872
0
    }
873
0
  }
874
0
  return slug;
875
0
}
876
877
/**
878
 * Parse the package author from the given `slug`
879
 */
880
881
4
char *clib_package_parse_author(const char *slug) {
882
4
  return parse_repo_owner(slug, DEFAULT_REPO_OWNER);
883
4
}
884
885
/**
886
 * Parse the package version from the given `slug`
887
 */
888
889
0
char *clib_package_parse_version(const char *slug) {
890
0
  return parse_repo_version(slug, DEFAULT_REPO_VERSION);
891
0
}
892
893
/**
894
 * Parse the package name from the given `slug`
895
 */
896
897
4
char *clib_package_parse_name(const char *slug) {
898
4
  return parse_repo_name(slug);
899
4
}
900
901
/**
902
 * Create a new package dependency from the given `repo` and `version`
903
 */
904
905
clib_package_dependency_t *clib_package_dependency_new(const char *repo,
906
4
                                                       const char *version) {
907
4
  if (!repo || !version)
908
0
    return NULL;
909
910
4
  clib_package_dependency_t *dep = malloc(sizeof(clib_package_dependency_t));
911
4
  if (!dep) {
912
0
    return NULL;
913
0
  }
914
915
4
  dep->version = 0 == strcmp("*", version) ? strdup(DEFAULT_REPO_VERSION)
916
4
                                           : strdup(version);
917
4
  dep->name = clib_package_parse_name(repo);
918
4
  dep->author = clib_package_parse_author(repo);
919
920
4
  _debug("dependency: %s/%s@%s", dep->author, dep->name, dep->version);
921
4
  return dep;
922
4
}
923
924
static int fetch_package_file_work(clib_package_t *pkg, const char *dir,
925
0
                                   char *file, int verbose) {
926
0
  char *url = NULL;
927
0
  char *path = NULL;
928
0
  int saved = 0;
929
0
  int rc = 0;
930
931
0
  _debug("fetch file: %s/%s", pkg->repo, file);
932
933
0
  if (NULL == pkg) {
934
0
    return 1;
935
0
  }
936
937
0
  if (NULL == pkg->url) {
938
0
    return 1;
939
0
  }
940
941
0
  if (0 == strncmp(file, "http", 4)) {
942
0
    url = strdup(file);
943
0
  } else if (!(url = clib_package_file_url(pkg->url, file))) {
944
0
    return 1;
945
0
  }
946
947
0
  _debug("file URL: %s", url);
948
949
0
  char *base_path = strdup(basename(file));
950
951
0
  if (!base_path) {
952
0
    rc = 1;
953
0
    goto cleanup;
954
0
  }
955
956
0
  path = path_join(dir, base_path);
957
958
0
  free(base_path);
959
960
0
  if (!path) {
961
0
    rc = 1;
962
0
    goto cleanup;
963
0
  }
964
965
0
#ifdef HAVE_PTHREADS
966
0
  pthread_mutex_lock(&lock.mutex);
967
0
#endif
968
969
0
  if (1 == opts.force || -1 == fs_exists(path)) {
970
0
    if (verbose) {
971
0
      logger_info("fetch", "%s:%s", pkg->repo, file);
972
0
      fflush(stdout);
973
0
    }
974
975
0
#ifdef HAVE_PTHREADS
976
0
    pthread_mutex_unlock(&lock.mutex);
977
0
#endif
978
979
0
    rc = http_get_file_shared(url, path, clib_package_curl_share);
980
0
    saved = 1;
981
0
  } else {
982
0
#ifdef HAVE_PTHREADS
983
0
    pthread_mutex_unlock(&lock.mutex);
984
0
#endif
985
0
  }
986
987
0
  if (-1 == rc) {
988
0
    if (verbose) {
989
0
#ifdef HAVE_PTHREADS
990
0
      pthread_mutex_lock(&lock.mutex);
991
0
#endif
992
0
      logger_error("error", "unable to fetch %s:%s", pkg->repo, file);
993
0
      fflush(stderr);
994
0
      rc = 1;
995
0
#ifdef HAVE_PTHREADS
996
0
      pthread_mutex_unlock(&lock.mutex);
997
0
#endif
998
0
      goto cleanup;
999
0
    }
1000
0
  }
1001
1002
0
  if (saved) {
1003
0
    if (verbose) {
1004
0
#ifdef HAVE_PTHREADS
1005
0
      pthread_mutex_lock(&lock.mutex);
1006
0
#endif
1007
0
      logger_info("save", path);
1008
0
      fflush(stdout);
1009
0
#ifdef HAVE_PTHREADS
1010
0
      pthread_mutex_unlock(&lock.mutex);
1011
0
#endif
1012
0
    }
1013
0
  }
1014
1015
0
cleanup:
1016
1017
0
  free(url);
1018
0
  free(path);
1019
0
  return rc;
1020
0
}
1021
1022
#ifdef HAVE_PTHREADS
1023
0
static void *fetch_package_file_thread(void *arg) {
1024
0
  fetch_package_file_thread_data_t *data = arg;
1025
0
  int *status = malloc(sizeof(int));
1026
0
  int rc =
1027
0
      fetch_package_file_work(data->pkg, data->dir, data->file, data->verbose);
1028
0
  *status = rc;
1029
0
  (void)data->pkg->refs--;
1030
0
  pthread_exit((void *)status);
1031
0
  return (void *)(intptr_t)rc;
1032
0
}
1033
#endif
1034
1035
/**
1036
 * Fetch a file associated with the given `pkg`.
1037
 *
1038
 * Returns 0 on success.
1039
 */
1040
1041
static int fetch_package_file(clib_package_t *pkg, const char *dir, char *file,
1042
0
                              int verbose, void **data) {
1043
#ifndef HAVE_PTHREADS
1044
  return fetch_package_file_work(pkg, dir, file, verbose);
1045
#else
1046
0
  fetch_package_file_thread_data_t *fetch = malloc(sizeof(*fetch));
1047
0
  int rc = 0;
1048
1049
0
  if (0 == fetch) {
1050
0
    return -1;
1051
0
  }
1052
1053
0
  *data = 0;
1054
1055
0
  memset(fetch, 0, sizeof(*fetch));
1056
1057
0
  fetch->pkg = pkg;
1058
0
  fetch->dir = dir;
1059
0
  fetch->file = file;
1060
0
  fetch->verbose = verbose;
1061
1062
0
  rc = pthread_attr_init(&fetch->attr);
1063
1064
0
  if (0 != rc) {
1065
0
    free(fetch);
1066
0
    return rc;
1067
0
  }
1068
1069
0
  (void)pkg->refs++;
1070
0
  rc = pthread_create(&fetch->thread, NULL, fetch_package_file_thread, fetch);
1071
1072
0
  if (0 != rc) {
1073
0
    pthread_attr_destroy(&fetch->attr);
1074
0
    free(fetch);
1075
0
    return rc;
1076
0
  }
1077
1078
0
  rc = pthread_attr_destroy(&fetch->attr);
1079
1080
0
  if (0 != rc) {
1081
0
    pthread_cancel(fetch->thread);
1082
0
    free(fetch);
1083
0
    return rc;
1084
0
  }
1085
1086
0
  *data = fetch;
1087
1088
0
  return rc;
1089
0
#endif
1090
0
}
1091
1092
0
static void set_prefix(clib_package_t *pkg, long path_max) {
1093
0
  if (NULL != opts.prefix || NULL != pkg->prefix) {
1094
0
    char path[path_max];
1095
0
    memset(path, 0, path_max);
1096
1097
0
    if (opts.prefix) {
1098
0
      realpath(opts.prefix, path);
1099
0
    } else {
1100
0
      realpath(pkg->prefix, path);
1101
0
    }
1102
1103
0
    _debug("env: PREFIX: %s", path);
1104
0
    setenv("PREFIX", path, 1);
1105
0
    mkdirp(path, 0777);
1106
0
  }
1107
0
}
1108
1109
int clib_package_install_executable(clib_package_t *pkg, const char *dir,
1110
0
                                    int verbose) {
1111
0
#ifdef PATH_MAX
1112
0
  long path_max = PATH_MAX;
1113
#elif defined(_PC_PATH_MAX)
1114
  long path_max = pathconf(dir, _PC_PATH_MAX);
1115
#else
1116
  long path_max = 4096;
1117
#endif
1118
1119
0
  int rc;
1120
0
  char *url = NULL;
1121
0
  char *file = NULL;
1122
0
  char *tarball = NULL;
1123
0
  char *command = NULL;
1124
0
  char *unpack_dir = NULL;
1125
0
  char *deps = NULL;
1126
0
  char *tmp = NULL;
1127
0
  char *reponame = NULL;
1128
0
  char dir_path[path_max];
1129
1130
0
  _debug("install executable %s", pkg->repo);
1131
1132
0
  tmp = gettempdir();
1133
1134
0
  if (NULL == tmp) {
1135
0
    if (verbose) {
1136
0
      logger_error("error", "gettempdir() out of memory");
1137
0
    }
1138
0
    return -1;
1139
0
  }
1140
1141
0
  if (!pkg->repo) {
1142
0
    if (verbose) {
1143
0
      logger_error("error", "repo field required to install executable");
1144
0
    }
1145
0
    return -1;
1146
0
  }
1147
1148
0
  reponame = strrchr(pkg->repo, '/');
1149
0
  if (reponame && *reponame != '\0')
1150
0
    reponame++;
1151
0
  else {
1152
0
    if (verbose) {
1153
0
      logger_error("error",
1154
0
                   "malformed repo field, must be in the form user/pkg");
1155
0
    }
1156
0
    return -1;
1157
0
  }
1158
1159
0
  E_FORMAT(&url, "https://github.com/%s/archive/%s.tar.gz", pkg->repo,
1160
0
           pkg->version);
1161
1162
0
  E_FORMAT(&file, "%s-%s.tar.gz", reponame, pkg->version);
1163
1164
0
  E_FORMAT(&tarball, "%s/%s", tmp, file);
1165
1166
0
  rc = http_get_file_shared(url, tarball, clib_package_curl_share);
1167
1168
0
  if (0 != rc) {
1169
0
    if (verbose) {
1170
0
      logger_error("error", "download failed for '%s@%s' - HTTP GET '%s'",
1171
0
                   pkg->repo, pkg->version, url);
1172
0
    }
1173
1174
0
    goto cleanup;
1175
0
  }
1176
1177
0
  E_FORMAT(&command, "cd %s && gzip -dc %s | tar x", tmp, file);
1178
1179
0
  _debug("download url: %s", url);
1180
0
  _debug("file: %s", file);
1181
0
  _debug("tarball: %s", tarball);
1182
0
  _debug("command(extract): %s", command);
1183
1184
  // cheap untar
1185
0
  rc = system(command);
1186
0
  if (0 != rc)
1187
0
    goto cleanup;
1188
1189
0
  free(command);
1190
0
  command = NULL;
1191
1192
0
  set_prefix(pkg, path_max);
1193
1194
0
  const char *configure = pkg->configure;
1195
1196
0
  if (0 == configure) {
1197
0
    configure = ":";
1198
0
  }
1199
1200
0
  memset(dir_path, 0, path_max);
1201
0
  realpath(dir, dir_path);
1202
1203
0
  char *version = pkg->version;
1204
0
  if ('v' == version[0]) {
1205
0
    (void)version++;
1206
0
  }
1207
1208
0
  E_FORMAT(&unpack_dir, "%s/%s-%s", tmp, reponame, version);
1209
1210
0
  _debug("dir: %s", unpack_dir);
1211
1212
0
  if (pkg->dependencies) {
1213
0
    E_FORMAT(&deps, "%s/deps", unpack_dir);
1214
0
    _debug("deps: %s", deps);
1215
0
    rc = clib_package_install_dependencies(pkg, deps, verbose);
1216
0
    if (-1 == rc)
1217
0
      goto cleanup;
1218
0
  }
1219
1220
0
  if (!opts.global && pkg->makefile) {
1221
0
    E_FORMAT(&command, "cp -fr %s/%s/%s %s", dir_path, pkg->name,
1222
0
             basename(pkg->makefile), unpack_dir);
1223
1224
0
    rc = system(command);
1225
0
    if (0 != rc) {
1226
0
      goto cleanup;
1227
0
    }
1228
1229
0
    free(command);
1230
0
  }
1231
1232
0
  if (pkg->flags) {
1233
0
    char *flags = NULL;
1234
#ifdef _GNU_SOURCE
1235
    char *cflags = secure_getenv("CFLAGS");
1236
#else
1237
0
    char *cflags = getenv("CFLAGS");
1238
0
#endif
1239
1240
0
    if (cflags) {
1241
0
      asprintf(&flags, "%s %s", cflags, pkg->flags);
1242
0
    } else {
1243
0
      asprintf(&flags, "%s", pkg->flags);
1244
0
    }
1245
1246
0
    setenv("CFLAGS", cflags, 1);
1247
0
  }
1248
1249
0
  E_FORMAT(&command, "cd %s && %s", unpack_dir, pkg->install);
1250
1251
0
  _debug("command(install): %s", command);
1252
0
  rc = system(command);
1253
1254
0
cleanup:
1255
0
  free(tmp);
1256
0
  free(command);
1257
0
  free(tarball);
1258
0
  free(file);
1259
0
  free(url);
1260
0
  return rc;
1261
0
}
1262
1263
/**
1264
 * Install the given `pkg` in `dir`
1265
 */
1266
1267
0
int clib_package_install(clib_package_t *pkg, const char *dir, int verbose) {
1268
0
  list_iterator_t *iterator = NULL;
1269
0
  char *package_json = NULL;
1270
0
  char *pkg_dir = NULL;
1271
0
  char *command = NULL;
1272
0
  int pending = 0;
1273
0
  int rc = 0;
1274
0
  int i = 0;
1275
1276
0
#ifdef PATH_MAX
1277
0
  long path_max = PATH_MAX;
1278
#elif defined(_PC_PATH_MAX)
1279
  long path_max = pathconf(dir, _PC_PATH_MAX);
1280
#else
1281
  long path_max = 4096;
1282
#endif
1283
1284
0
#ifdef HAVE_PTHREADS
1285
0
  int max = opts.concurrency;
1286
0
#endif
1287
1288
#ifdef CLIB_PACKAGE_PREFIX
1289
  if (0 == opts.prefix) {
1290
#ifdef HAVE_PTHREADS
1291
    pthread_mutex_lock(&lock.mutex);
1292
#endif
1293
    opts.prefix = CLIB_PACKAGE_PREFIX;
1294
#ifdef HAVE_PTHREADS
1295
    pthread_mutex_unlock(&lock.mutex);
1296
#endif
1297
  }
1298
#endif
1299
1300
0
  if (0 == opts.prefix) {
1301
0
#ifdef HAVE_PTHREADS
1302
0
    pthread_mutex_lock(&lock.mutex);
1303
0
#endif
1304
#ifdef _GNU_SOURCE
1305
    char *prefix = secure_getenv("PREFIX");
1306
#else
1307
0
    char *prefix = getenv("PREFIX");
1308
0
#endif
1309
1310
0
    if (prefix) {
1311
0
      opts.prefix = prefix;
1312
0
    }
1313
0
#ifdef HAVE_PTHREADS
1314
0
    pthread_mutex_unlock(&lock.mutex);
1315
0
#endif
1316
0
  }
1317
1318
0
  if (0 == visited_packages) {
1319
0
#ifdef HAVE_PTHREADS
1320
0
    pthread_mutex_lock(&lock.mutex);
1321
0
#endif
1322
1323
0
    visited_packages = hash_new();
1324
    // initial write because sometimes `hash_set()` crashes
1325
0
    hash_set(visited_packages, strdup(""), "");
1326
1327
0
#ifdef HAVE_PTHREADS
1328
0
    pthread_mutex_unlock(&lock.mutex);
1329
0
#endif
1330
0
  }
1331
1332
0
  if (0 == opts.force && pkg && pkg->name) {
1333
0
#ifdef HAVE_PTHREADS
1334
0
    pthread_mutex_lock(&lock.mutex);
1335
0
#endif
1336
1337
0
    if (hash_has(visited_packages, pkg->name)) {
1338
0
#ifdef HAVE_PTHREADS
1339
0
      pthread_mutex_unlock(&lock.mutex);
1340
0
#endif
1341
0
      return 0;
1342
0
    }
1343
1344
0
#ifdef HAVE_PTHREADS
1345
0
    pthread_mutex_unlock(&lock.mutex);
1346
0
#endif
1347
0
  }
1348
1349
0
#ifdef HAVE_PTHREADS
1350
0
  fetch_package_file_thread_data_t **fetchs = 0;
1351
0
  if (NULL != pkg && NULL != pkg->src) {
1352
0
    if (pkg->src->len > 0) {
1353
0
      fetchs = malloc(pkg->src->len * sizeof(fetch_package_file_thread_data_t));
1354
0
    }
1355
0
  }
1356
1357
0
  if (fetchs) {
1358
0
    memset(fetchs, 0, pkg->src->len * sizeof(fetch_package_file_thread_data_t));
1359
0
  }
1360
1361
0
#endif
1362
1363
0
  if (!pkg || !dir) {
1364
0
    rc = -1;
1365
0
    goto cleanup;
1366
0
  }
1367
1368
0
  set_prefix(pkg, path_max);
1369
1370
0
  if (!(pkg_dir = path_join(dir, pkg->name))) {
1371
0
    rc = -1;
1372
0
    goto cleanup;
1373
0
  }
1374
1375
0
  if (!opts.global) {
1376
0
    _debug("mkdir -p %s", pkg_dir);
1377
    // create directory for pkg
1378
0
    if (-1 == mkdirp(pkg_dir, 0777)) {
1379
0
      rc = -1;
1380
0
      goto cleanup;
1381
0
    }
1382
0
  }
1383
1384
0
  if (NULL == pkg->url) {
1385
0
    pkg->url = clib_package_url(pkg->author, pkg->repo_name, pkg->version);
1386
1387
0
    if (NULL == pkg->url) {
1388
0
      rc = -1;
1389
0
      goto cleanup;
1390
0
    }
1391
0
  }
1392
1393
  // write clib.json or package.json
1394
0
  if (!(package_json = path_join(pkg_dir, pkg->filename))) {
1395
0
    rc = -1;
1396
0
    goto cleanup;
1397
0
  }
1398
1399
0
  if (!opts.global && NULL != pkg->src) {
1400
0
    _debug("write: %s", package_json);
1401
0
    if (-1 == fs_write(package_json, pkg->json)) {
1402
0
      if (verbose) {
1403
0
        logger_error("error", "Failed to write %s", package_json);
1404
0
      }
1405
1406
0
      rc = -1;
1407
0
      goto cleanup;
1408
0
    }
1409
0
  }
1410
1411
0
  if (pkg->name) {
1412
0
#ifdef HAVE_PTHREADS
1413
0
    pthread_mutex_lock(&lock.mutex);
1414
0
#endif
1415
0
    if (!hash_has(visited_packages, pkg->name)) {
1416
0
      hash_set(visited_packages, strdup(pkg->name), "t");
1417
0
    }
1418
0
#ifdef HAVE_PTHREADS
1419
0
    pthread_mutex_unlock(&lock.mutex);
1420
0
#endif
1421
0
  }
1422
1423
  // fetch makefile
1424
0
  if (!opts.global && pkg->makefile) {
1425
0
    _debug("fetch: %s/%s", pkg->repo, pkg->makefile);
1426
0
    void *fetch = 0;
1427
0
    rc = fetch_package_file(pkg, pkg_dir, pkg->makefile, verbose, &fetch);
1428
0
    if (0 != rc) {
1429
0
      goto cleanup;
1430
0
    }
1431
1432
0
#ifdef HAVE_PTHREADS
1433
0
    if (0 != fetch) {
1434
0
      fetch_package_file_thread_data_t *data = fetch;
1435
0
      int *status;
1436
0
      pthread_join(data->thread, (void **)&status);
1437
0
      if (NULL != status) {
1438
0
        rc = *status;
1439
0
        free(status);
1440
0
        status = 0;
1441
0
        if (0 != rc) {
1442
0
          rc = 0;
1443
0
          logger_warn("warning", "unable to fetch Makefile (%s) for '%s'",
1444
0
                      pkg->makefile, pkg->name);
1445
0
        }
1446
0
      }
1447
0
    }
1448
0
#endif
1449
0
  }
1450
1451
  // if no sources are listed, just install
1452
0
  if (opts.global || NULL == pkg->src)
1453
0
    goto install;
1454
1455
0
#ifdef HAVE_PTHREADS
1456
0
  pthread_mutex_lock(&lock.mutex);
1457
0
#endif
1458
1459
0
  if (clib_cache_has_package(pkg->author, pkg->name, pkg->version)) {
1460
0
    if (opts.skip_cache) {
1461
0
      clib_cache_delete_package(pkg->author, pkg->name, pkg->version);
1462
0
#ifdef HAVE_PTHREADS
1463
0
      pthread_mutex_unlock(&lock.mutex);
1464
0
#endif
1465
0
      goto download;
1466
0
    }
1467
1468
0
    if (0 != clib_cache_load_package(pkg->author, pkg->name, pkg->version,
1469
0
                                     pkg_dir)) {
1470
0
#ifdef HAVE_PTHREADS
1471
0
      pthread_mutex_unlock(&lock.mutex);
1472
0
#endif
1473
0
      goto download;
1474
0
    }
1475
1476
0
    if (verbose) {
1477
0
      logger_info("cache", pkg->repo);
1478
0
    }
1479
1480
0
#ifdef HAVE_PTHREADS
1481
0
    pthread_mutex_unlock(&lock.mutex);
1482
0
#endif
1483
1484
0
    goto install;
1485
0
  }
1486
1487
0
#ifdef HAVE_PTHREADS
1488
0
  pthread_mutex_unlock(&lock.mutex);
1489
0
#endif
1490
1491
0
download:
1492
1493
0
  iterator = list_iterator_new(pkg->src, LIST_HEAD);
1494
0
  list_node_t *source;
1495
1496
0
  while ((source = list_iterator_next(iterator))) {
1497
0
    void *fetch = NULL;
1498
0
    rc = fetch_package_file(pkg, pkg_dir, source->val, verbose, &fetch);
1499
1500
0
    if (0 != rc) {
1501
0
      list_iterator_destroy(iterator);
1502
0
      iterator = NULL;
1503
0
      rc = -1;
1504
0
      goto cleanup;
1505
0
    }
1506
1507
0
#ifdef HAVE_PTHREADS
1508
0
    if (i < 0) {
1509
0
      i = 0;
1510
0
    }
1511
1512
0
    fetchs[i] = fetch;
1513
1514
0
    (void)pending++;
1515
1516
0
    if (i < (max - 1)) {
1517
0
      (void)i++;
1518
0
    } else {
1519
0
      for (int j = 0; j <= i; j++) {
1520
0
        fetch_package_file_thread_data_t *data = fetchs[j];
1521
0
        int *status;
1522
0
        pthread_join(data->thread, (void **)&status);
1523
0
        free(data);
1524
0
        fetchs[j] = NULL;
1525
1526
0
        (void)pending--;
1527
1528
0
        if (NULL != status) {
1529
0
          rc = *status;
1530
0
          free(status);
1531
0
          status = 0;
1532
0
        }
1533
1534
0
        if (0 != rc) {
1535
0
          rc = -1;
1536
0
          goto cleanup;
1537
0
        }
1538
0
      }
1539
0
      i = 0;
1540
0
    }
1541
0
#endif
1542
0
  }
1543
1544
0
#ifdef HAVE_PTHREADS
1545
  // Here there are i-1 threads running.
1546
0
  for (int j = 0; j < i; j++) {
1547
0
    fetch_package_file_thread_data_t *data = fetchs[j];
1548
0
    int *status;
1549
1550
0
    pthread_join(data->thread, (void **)&status);
1551
1552
0
    (void)pending--;
1553
0
    free(data);
1554
0
    fetchs[j] = NULL;
1555
1556
0
    if (NULL != status) {
1557
0
      rc = *status;
1558
0
      free(status);
1559
0
      status = 0;
1560
0
    }
1561
1562
0
    if (0 != rc) {
1563
0
      rc = -1;
1564
0
      goto cleanup;
1565
0
    }
1566
0
  }
1567
0
#endif
1568
1569
0
install:
1570
0
  if (pkg->configure) {
1571
0
    E_FORMAT(&command, "cd %s/%s && %s", dir, pkg->name, pkg->configure);
1572
1573
0
    _debug("command(configure): %s", command);
1574
1575
0
    rc = system(command);
1576
0
    if (0 != rc)
1577
0
      goto cleanup;
1578
0
  }
1579
1580
0
  if (0 == rc && pkg->install) {
1581
0
    rc = clib_package_install_executable(pkg, dir, verbose);
1582
0
  }
1583
1584
0
  if (0 == rc) {
1585
0
    rc = clib_package_install_dependencies(pkg, dir, verbose);
1586
0
  }
1587
1588
0
#ifdef HAVE_PTHREADS
1589
0
  pthread_mutex_lock(&lock.mutex);
1590
0
#endif
1591
0
  if (0 == rc) {
1592
0
    clib_cache_save_package(pkg->author, pkg->name, pkg->version, pkg_dir);
1593
0
    _debug("cached package: %s/%s@%s", pkg->author, pkg->name, pkg->version);
1594
0
  }
1595
0
#ifdef HAVE_PTHREADS
1596
0
  pthread_mutex_unlock(&lock.mutex);
1597
0
#endif
1598
1599
0
cleanup:
1600
0
  if (pkg_dir) {
1601
0
    if (0 != rc) {
1602
0
      rimraf(pkg_dir);
1603
0
      _debug("deleted inconsistent package dir: %s", pkg_dir);
1604
0
    }
1605
1606
0
    free(pkg_dir);
1607
0
  }
1608
0
  if (package_json)
1609
0
    free(package_json);
1610
0
  if (iterator)
1611
0
    list_iterator_destroy(iterator);
1612
0
  if (command)
1613
0
    free(command);
1614
0
#ifdef HAVE_PTHREADS
1615
0
  if (NULL != pkg && NULL != pkg->src) {
1616
0
    if (pkg->src->len > 0) {
1617
0
      if (fetchs) {
1618
0
        free(fetchs);
1619
0
      }
1620
0
    }
1621
0
  }
1622
0
  fetchs = NULL;
1623
0
#endif
1624
1625
0
#ifdef HAVE_PTHREADS
1626
0
  pthread_mutex_lock(&lock.mutex);
1627
0
#endif
1628
0
  if (0 != rc && pkg) {
1629
0
    clib_cache_delete_json(pkg->author, pkg->name, pkg->version);
1630
0
    _debug("deleted json cache: %s/%s@%s", pkg->author, pkg->name,
1631
0
           pkg->version);
1632
0
  }
1633
0
#ifdef HAVE_PTHREADS
1634
0
  pthread_mutex_unlock(&lock.mutex);
1635
0
#endif
1636
1637
0
  return rc;
1638
0
}
1639
1640
/**
1641
 * Install the given `pkg`'s dependencies in `dir`
1642
 */
1643
1644
int clib_package_install_dependencies(clib_package_t *pkg, const char *dir,
1645
0
                                      int verbose) {
1646
0
  if (!pkg || !dir)
1647
0
    return -1;
1648
0
  if (NULL == pkg->dependencies)
1649
0
    return 0;
1650
1651
0
  return install_packages(pkg->dependencies, dir, verbose);
1652
0
}
1653
1654
/**
1655
 * Install the given `pkg`'s development dependencies in `dir`
1656
 */
1657
1658
int clib_package_install_development(clib_package_t *pkg, const char *dir,
1659
0
                                     int verbose) {
1660
0
  if (!pkg || !dir)
1661
0
    return -1;
1662
0
  if (NULL == pkg->development)
1663
0
    return 0;
1664
1665
0
  return install_packages(pkg->development, dir, verbose);
1666
0
}
1667
1668
/**
1669
 * Free a clib package
1670
 */
1671
1672
5
void clib_package_free(clib_package_t *pkg) {
1673
5
  if (NULL == pkg) {
1674
0
    return;
1675
0
  }
1676
1677
5
  if (0 != pkg->refs) {
1678
0
    return;
1679
0
  }
1680
1681
5
#define FREE(k)                                                                \
1682
65
  if (pkg->k) {                                                                \
1683
6
    free(pkg->k);                                                              \
1684
6
    pkg->k = 0;                                                                \
1685
6
  }
1686
5
  FREE(author);
1687
5
  FREE(description);
1688
5
  FREE(install);
1689
5
  FREE(json);
1690
5
  FREE(license);
1691
5
  FREE(name);
1692
5
  FREE(makefile);
1693
5
  FREE(configure);
1694
5
  FREE(repo);
1695
5
  FREE(repo_name);
1696
5
  FREE(url);
1697
5
  FREE(version);
1698
5
  FREE(flags);
1699
5
#undef FREE
1700
1701
5
  if (pkg->src)
1702
0
    list_destroy(pkg->src);
1703
5
  pkg->src = 0;
1704
1705
5
  if (pkg->dependencies)
1706
0
    list_destroy(pkg->dependencies);
1707
5
  pkg->dependencies = 0;
1708
1709
5
  if (pkg->development)
1710
1
    list_destroy(pkg->development);
1711
5
  pkg->development = 0;
1712
1713
5
  free(pkg);
1714
5
  pkg = 0;
1715
5
}
1716
1717
4
void clib_package_dependency_free(void *_dep) {
1718
4
  clib_package_dependency_t *dep = (clib_package_dependency_t *)_dep;
1719
4
  free(dep->name);
1720
4
  free(dep->author);
1721
4
  free(dep->version);
1722
4
  free(dep);
1723
4
}
1724
1725
0
void clib_package_cleanup() {
1726
0
  if (0 != visited_packages) {
1727
0
    hash_each(visited_packages, {
1728
0
      free((void *)key);
1729
0
      (void)val;
1730
0
    });
1731
1732
0
    hash_free(visited_packages);
1733
0
    visited_packages = 0;
1734
0
  }
1735
1736
0
  curl_share_cleanup(clib_package_curl_share);
1737
0
}