1174 lines
28 KiB
C
1174 lines
28 KiB
C
|
|
/******************************************************************************
|
||
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
|
||
|
|
* iSulad licensed under the Mulan PSL v1.
|
||
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v1.
|
||
|
|
* You may obtain a copy of Mulan PSL v1 at:
|
||
|
|
* http://license.coscl.org.cn/MulanPSL
|
||
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
|
|
* PURPOSE.
|
||
|
|
* See the Mulan PSL v1 for more details.
|
||
|
|
* Author: tanyifeng
|
||
|
|
* Create: 2017-11-22
|
||
|
|
* Description: provide image functions
|
||
|
|
******************************************************************************/
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <stdarg.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <limits.h>
|
||
|
|
#include <sys/utsname.h>
|
||
|
|
#include <ctype.h>
|
||
|
|
|
||
|
|
#include "securec.h"
|
||
|
|
#include "image.h"
|
||
|
|
#include "liblcrd.h"
|
||
|
|
#include "log.h"
|
||
|
|
#include "utils.h"
|
||
|
|
|
||
|
|
#include "ext_image.h"
|
||
|
|
|
||
|
|
#ifdef ENABLE_OCI_IMAGE
|
||
|
|
#include "oci_image.h"
|
||
|
|
#include "oci_image_status.h"
|
||
|
|
#include "oci_image_load.h"
|
||
|
|
#include "oci_login.h"
|
||
|
|
#include "oci_logout.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef ENABLE_EMBEDDED_IMAGE
|
||
|
|
#include "embedded_image.h"
|
||
|
|
#include "db_all.h"
|
||
|
|
|
||
|
|
/* embedded */
|
||
|
|
static const struct bim_ops g_embedded_ops = {
|
||
|
|
.init = embedded_init,
|
||
|
|
.detect = embedded_detect,
|
||
|
|
|
||
|
|
.prepare_rf = embedded_prepare_rf,
|
||
|
|
.mount_rf = embedded_mount_rf,
|
||
|
|
.umount_rf = embedded_umount_rf,
|
||
|
|
.delete_rf = embedded_delete_rf,
|
||
|
|
|
||
|
|
.merge_conf = embedded_merge_conf,
|
||
|
|
.get_user_conf = embedded_get_user_conf,
|
||
|
|
|
||
|
|
.list_ims = embedded_list_images,
|
||
|
|
.rm_image = embedded_remove_image,
|
||
|
|
.inspect_image = embedded_inspect_image,
|
||
|
|
.resolve_image_name = embedded_resolve_image_name,
|
||
|
|
.filesystem_usage = embedded_filesystem_usage,
|
||
|
|
.load_image = embedded_load_image,
|
||
|
|
.pull_image = NULL,
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef ENABLE_OCI_IMAGE
|
||
|
|
/* oci */
|
||
|
|
static const struct bim_ops g_oci_ops = {
|
||
|
|
.init = oci_init,
|
||
|
|
.detect = oci_detect,
|
||
|
|
|
||
|
|
.prepare_rf = oci_prepare_rf,
|
||
|
|
.mount_rf = oci_mount_rf,
|
||
|
|
.umount_rf = oci_umount_rf,
|
||
|
|
.delete_rf = oci_delete_rf,
|
||
|
|
|
||
|
|
.merge_conf = oci_merge_conf,
|
||
|
|
.get_user_conf = oci_get_user_conf,
|
||
|
|
|
||
|
|
.list_ims = oci_list_images,
|
||
|
|
.rm_image = oci_remove_image,
|
||
|
|
.inspect_image = oci_inspect_image,
|
||
|
|
.resolve_image_name = oci_resolve_image_name,
|
||
|
|
.filesystem_usage = oci_filesystem_usage,
|
||
|
|
.load_image = oci_load_image,
|
||
|
|
.pull_image = oci_pull_image,
|
||
|
|
.login = oci_login,
|
||
|
|
.logout = oci_logout,
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* external */
|
||
|
|
static const struct bim_ops g_ext_ops = {
|
||
|
|
.init = ext_init,
|
||
|
|
.detect = ext_detect,
|
||
|
|
|
||
|
|
.prepare_rf = ext_prepare_rf,
|
||
|
|
.mount_rf = ext_mount_rf,
|
||
|
|
.umount_rf = ext_umount_rf,
|
||
|
|
.delete_rf = ext_delete_rf,
|
||
|
|
|
||
|
|
.merge_conf = ext_merge_conf,
|
||
|
|
.get_user_conf = ext_get_user_conf,
|
||
|
|
|
||
|
|
.list_ims = ext_list_images,
|
||
|
|
.rm_image = ext_remove_image,
|
||
|
|
.inspect_image = ext_inspect_image,
|
||
|
|
.resolve_image_name = ext_resolve_image_name,
|
||
|
|
.filesystem_usage = ext_filesystem_usage,
|
||
|
|
.load_image = ext_load_image,
|
||
|
|
.pull_image = NULL,
|
||
|
|
.login = ext_login,
|
||
|
|
.logout = ext_logout,
|
||
|
|
};
|
||
|
|
|
||
|
|
static const struct bim_type g_bims[] = {
|
||
|
|
#ifdef ENABLE_OCI_IMAGE
|
||
|
|
{
|
||
|
|
.image_type = IMAGE_TYPE_OCI,
|
||
|
|
.ops = &g_oci_ops,
|
||
|
|
},
|
||
|
|
#endif
|
||
|
|
{ .image_type = IMAGE_TYPE_EXTERNAL, .ops = &g_ext_ops },
|
||
|
|
#ifdef ENABLE_EMBEDDED_IMAGE
|
||
|
|
{ .image_type = IMAGE_TYPE_EMBEDDED, .ops = &g_embedded_ops },
|
||
|
|
#endif
|
||
|
|
};
|
||
|
|
|
||
|
|
static const size_t g_numbims = sizeof(g_bims) / sizeof(struct bim_type);
|
||
|
|
|
||
|
|
static const struct bim_type *bim_query(const char *image_name)
|
||
|
|
{
|
||
|
|
size_t i;
|
||
|
|
char *temp = NULL;
|
||
|
|
|
||
|
|
for (i = 0; i < g_numbims; i++) {
|
||
|
|
temp = g_bims[i].ops->resolve_image_name(image_name);
|
||
|
|
if (temp == NULL) {
|
||
|
|
lcrd_append_error_message("Failed to resovle image name%s", image_name);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
int r = g_bims[i].ops->detect(temp);
|
||
|
|
|
||
|
|
free(temp);
|
||
|
|
temp = NULL;
|
||
|
|
|
||
|
|
if (r != 0) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (i == g_numbims) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
return &g_bims[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
static const struct bim_type *get_bim_by_type(const char *image_type)
|
||
|
|
{
|
||
|
|
size_t i;
|
||
|
|
|
||
|
|
for (i = 0; i < g_numbims; i++) {
|
||
|
|
if (strcmp(g_bims[i].image_type, image_type) == 0) {
|
||
|
|
return &g_bims[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ERROR("Backing store %s unknown but not caught earlier\n", image_type);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void bim_put(struct bim *bim)
|
||
|
|
{
|
||
|
|
if (bim == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(bim->image_name);
|
||
|
|
bim->image_name = NULL;
|
||
|
|
free(bim->ext_config_image);
|
||
|
|
bim->ext_config_image = NULL;
|
||
|
|
free(bim->container_id);
|
||
|
|
bim->container_id = NULL;
|
||
|
|
free(bim);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bim *bim_get(const char *image_type, const char *image_name, const char *ext_config_image,
|
||
|
|
const char *container_id)
|
||
|
|
{
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
const struct bim_type *q = NULL;
|
||
|
|
|
||
|
|
if (image_type == NULL) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
q = get_bim_by_type(image_type);
|
||
|
|
if (q == NULL) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = util_common_calloc_s(sizeof(struct bim));
|
||
|
|
if (bim == NULL) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim->ops = q->ops;
|
||
|
|
bim->type = q->image_type;
|
||
|
|
|
||
|
|
if (image_name != NULL) {
|
||
|
|
bim->image_name = bim->ops->resolve_image_name(image_name);
|
||
|
|
if (bim->image_name == NULL) {
|
||
|
|
lcrd_append_error_message("Failed to resovle image name%s", bim->image_name);
|
||
|
|
bim_put(bim);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (ext_config_image != NULL) {
|
||
|
|
bim->ext_config_image = util_strdup_s(ext_config_image);
|
||
|
|
if (bim->ext_config_image == NULL) {
|
||
|
|
lcrd_append_error_message("Failed to strdup external config image %s", bim->ext_config_image);
|
||
|
|
bim_put(bim);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (container_id != NULL) {
|
||
|
|
bim->container_id = util_strdup_s(container_id);
|
||
|
|
}
|
||
|
|
return bim;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_get_container_filesystem_usage(const char *image_type, const char *id, imagetool_fs_info **fs_usage)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
imagetool_fs_info *filesystemusage = NULL;
|
||
|
|
const struct bim_type *q = NULL;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (image_type == NULL || id == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
q = get_bim_by_type(image_type);
|
||
|
|
if (q == NULL) {
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = util_common_calloc_s(sizeof(struct bim));
|
||
|
|
if (bim == NULL) {
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim->ops = q->ops;
|
||
|
|
bim->type = q->image_type;
|
||
|
|
|
||
|
|
if (id != NULL) {
|
||
|
|
bim->container_id = util_strdup_s(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->filesystem_usage(bim, &filesystemusage);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to get filesystem usage for container %s", id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
*fs_usage = filesystemusage;
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_remove_container_rootfs(const char *image_type, const char *container_id)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (container_id == NULL || image_type == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(image_type, NULL, NULL, container_id);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim for container %s", container_id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->delete_rf(bim);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to delete rootfs for container %s", container_id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_umount_container_rootfs(const char *image_type, const char *image_name, const char *container_id)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (container_id == NULL || image_type == NULL || image_name == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(image_type, image_name, NULL, container_id);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim for container %s", container_id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->umount_rf(bim);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to umount rootfs for container %s", container_id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_mount_container_rootfs(const char *image_type, const char *image_name, const char *container_id)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (image_name == NULL || container_id == NULL || image_type == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(image_type, image_name, NULL, container_id);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim for container %s", container_id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->mount_rf(bim);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to mount rootfs for container %s", container_id);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
char *im_get_image_type(const char *image, const char *external_rootfs)
|
||
|
|
{
|
||
|
|
const char *image_name = NULL;
|
||
|
|
const struct bim_type *bim_type = NULL;
|
||
|
|
|
||
|
|
image_name = (external_rootfs != NULL) ? external_rootfs : image;
|
||
|
|
if (image_name == NULL) {
|
||
|
|
ERROR("Should specify the image name or external rootfs");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim_type = bim_query(image_name);
|
||
|
|
if (bim_type == NULL) {
|
||
|
|
ERROR("Failed to query type of image %s", image_name);
|
||
|
|
lcrd_set_error_message("No such image:%s", image_name);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return util_strdup_s(bim_type->image_type);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool im_config_image_exist(const char *image_name)
|
||
|
|
{
|
||
|
|
const struct bim_type *bim_type = NULL;
|
||
|
|
|
||
|
|
bim_type = bim_query(image_name);
|
||
|
|
if (bim_type == NULL) {
|
||
|
|
ERROR("Config image %s not exist", image_name);
|
||
|
|
lcrd_set_error_message("Image %s not exist", image_name);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_merge_image_config(const char *id, const char *image_type, const char *image_name,
|
||
|
|
const char *ext_config_image, oci_runtime_spec *oci_spec,
|
||
|
|
host_config *host_spec, container_custom_config *custom_spec,
|
||
|
|
char **real_rootfs)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (real_rootfs == NULL || oci_spec == NULL || image_type == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(image_type, image_name, ext_config_image, id);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim of image %s", image_name);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->merge_conf(oci_spec, host_spec, custom_spec, bim, real_rootfs);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to merge image %s config, config image is %s", image_name, ext_config_image);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
INFO("Use real rootfs: %s with type: %s", *real_rootfs, image_type);
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_get_user_conf(const char *image_type, const char *basefs, host_config *hc, const char *userstr,
|
||
|
|
oci_runtime_spec_process_user *puser)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (basefs == NULL || hc == NULL || image_type == NULL || puser == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(image_type, NULL, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim for image type: %s", image_type);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->get_user_conf(basefs, hc, userstr, puser);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to get user config");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int append_images_to_response(im_list_response *response, imagetool_images_list *images_in)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
size_t images_num = 0;
|
||
|
|
size_t old_num = 0;
|
||
|
|
imagetool_image **tmp = NULL;
|
||
|
|
size_t i = 0;
|
||
|
|
size_t new_size = 0;
|
||
|
|
size_t old_size = 0;
|
||
|
|
|
||
|
|
if (images_in == NULL || response == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (response->images == NULL) {
|
||
|
|
response->images = util_common_calloc_s(sizeof(imagetool_images_list));
|
||
|
|
if (response->images == NULL) {
|
||
|
|
ERROR("Memeory out");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
images_num = images_in->images_len;
|
||
|
|
|
||
|
|
// no images need to append
|
||
|
|
if (images_num == 0) {
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
if (images_num > SIZE_MAX / sizeof(imagetool_image *) - response->images->images_len) {
|
||
|
|
ERROR("Too many images to append!");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
old_num = response->images->images_len;
|
||
|
|
|
||
|
|
new_size = (old_num + images_num) * sizeof(imagetool_image *);
|
||
|
|
old_size = old_num * sizeof(imagetool_image *);
|
||
|
|
ret = mem_realloc((void **)(&tmp), new_size, response->images->images, old_size);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to realloc memory for append images");
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
response->images->images = tmp;
|
||
|
|
for (i = 0; i < images_num; i++) {
|
||
|
|
response->images->images[old_num + i] = images_in->images[i];
|
||
|
|
images_in->images[i] = NULL;
|
||
|
|
images_in->images_len--;
|
||
|
|
response->images->images_len++;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_list_images(im_list_request *request, im_list_response **response)
|
||
|
|
{
|
||
|
|
char *filter = NULL;
|
||
|
|
size_t i;
|
||
|
|
imagetool_images_list *images_tmp = NULL;
|
||
|
|
|
||
|
|
filter = request->filter.image.image;
|
||
|
|
|
||
|
|
*response = util_common_calloc_s(sizeof(im_list_response));
|
||
|
|
if (*response == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: list images, Type: listing, Filter: %s}", filter ? filter : "");
|
||
|
|
|
||
|
|
for (i = 0; i < g_numbims; i++) {
|
||
|
|
int ret = g_bims[i].ops->list_ims(request, &images_tmp);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to list all images with type:%s", g_bims[i].image_type);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
ret = append_images_to_response(*response, images_tmp);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to append images with type:%s", g_bims[i].image_type);
|
||
|
|
}
|
||
|
|
free_imagetool_images_list(images_tmp);
|
||
|
|
images_tmp = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: list images, Type: listed, Filter: %s}", filter ? filter : "");
|
||
|
|
|
||
|
|
if (g_lcrd_errmsg != NULL) {
|
||
|
|
(*response)->errmsg = util_strdup_s(g_lcrd_errmsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_list_request(im_list_request *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
free(ptr->filter.image.image);
|
||
|
|
ptr->filter.image.image = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_list_response(im_list_response *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
free_imagetool_images_list(ptr->images);
|
||
|
|
ptr->images = NULL;
|
||
|
|
free(ptr->errmsg);
|
||
|
|
ptr->errmsg = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool check_im_pull_args(const im_pull_request *req, im_pull_response * const *resp)
|
||
|
|
{
|
||
|
|
if (req == NULL || resp == NULL) {
|
||
|
|
ERROR("Request or response is NULL");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (req->image == NULL) {
|
||
|
|
ERROR("Empty image required");
|
||
|
|
lcrd_set_error_message("Empty image required");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_pull_image(const im_pull_request *request, im_pull_response **response)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (!check_im_pull_args(request, response)) {
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(request->type, NULL, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim, image type: %s", request->type);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (bim->ops->pull_image == NULL) {
|
||
|
|
WARN("Unimplements pull image in %s", bim->type);
|
||
|
|
ret = 0;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->pull_image(request, response);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Pull image %s failed", request->image);
|
||
|
|
ret = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_pull_request(im_pull_request *req)
|
||
|
|
{
|
||
|
|
if (req == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
free(req->type);
|
||
|
|
req->type = NULL;
|
||
|
|
free(req->image);
|
||
|
|
req->image = NULL;
|
||
|
|
free_sensitive_string(req->username);
|
||
|
|
req->username = NULL;
|
||
|
|
free_sensitive_string(req->password);
|
||
|
|
req->password = NULL;
|
||
|
|
free_sensitive_string(req->auth);
|
||
|
|
req->auth = NULL;
|
||
|
|
free_sensitive_string(req->server_address);
|
||
|
|
req->server_address = NULL;
|
||
|
|
free_sensitive_string(req->registry_token);
|
||
|
|
req->registry_token = NULL;
|
||
|
|
free_sensitive_string(req->identity_token);
|
||
|
|
req->identity_token = NULL;
|
||
|
|
free(req);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_pull_response(im_pull_response *resp)
|
||
|
|
{
|
||
|
|
if (resp == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
free(resp->image_ref);
|
||
|
|
resp->image_ref = NULL;
|
||
|
|
free(resp->errmsg);
|
||
|
|
resp->errmsg = NULL;
|
||
|
|
free(resp);
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_load_image(im_load_request *request, im_load_response **response)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (request == NULL || response == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
*response = util_common_calloc_s(sizeof(im_load_response));
|
||
|
|
if (*response == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->file == NULL) {
|
||
|
|
ERROR("Load image requires image tarball file path");
|
||
|
|
lcrd_set_error_message("Load image requires image tarball file path");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->type == NULL) {
|
||
|
|
ERROR("Missing image type");
|
||
|
|
lcrd_set_error_message("Missing image type");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(request->type, NULL, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim, image type:%s", request->type);
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: loading}", request->file);
|
||
|
|
|
||
|
|
ret = bim->ops->load_image(request);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to load image from %s with tag %s and type %s", request->file, request->tag, request->type);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: loaded}", request->file);
|
||
|
|
|
||
|
|
pack_response:
|
||
|
|
if (g_lcrd_errmsg != NULL) {
|
||
|
|
(*response)->errmsg = util_strdup_s(g_lcrd_errmsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_load_request(im_load_request *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->file);
|
||
|
|
ptr->file = NULL;
|
||
|
|
|
||
|
|
free(ptr->tag);
|
||
|
|
ptr->file = NULL;
|
||
|
|
|
||
|
|
free(ptr->type);
|
||
|
|
ptr->type = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_load_response(im_load_response *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->errmsg);
|
||
|
|
ptr->errmsg = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_login(im_login_request *request, im_login_response **response)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (request == NULL || response == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
*response = util_common_calloc_s(sizeof(im_login_response));
|
||
|
|
if (*response == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->server == NULL) {
|
||
|
|
ERROR("Login requires server address");
|
||
|
|
lcrd_set_error_message("Login requires server address");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->type == NULL) {
|
||
|
|
ERROR("Login requires image type");
|
||
|
|
lcrd_set_error_message("Login requires image type");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->username == NULL || request->password == NULL) {
|
||
|
|
ERROR("Missing username or password");
|
||
|
|
lcrd_set_error_message("Missing username or password");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(request->type, NULL, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim, image type:%s", request->type);
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: logining}", request->server);
|
||
|
|
|
||
|
|
ret = bim->ops->login(request);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to login %s", request->server);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: logined}", request->server);
|
||
|
|
|
||
|
|
pack_response:
|
||
|
|
if (g_lcrd_errmsg != NULL) {
|
||
|
|
(*response)->errmsg = util_strdup_s(g_lcrd_errmsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_login_request(im_login_request *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free_sensitive_string(ptr->username);
|
||
|
|
ptr->username = NULL;
|
||
|
|
|
||
|
|
free_sensitive_string(ptr->password);
|
||
|
|
ptr->password = NULL;
|
||
|
|
|
||
|
|
free(ptr->type);
|
||
|
|
ptr->type = NULL;
|
||
|
|
|
||
|
|
free_sensitive_string(ptr->server);
|
||
|
|
ptr->server = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_login_response(im_login_response *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->errmsg);
|
||
|
|
ptr->errmsg = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_logout(im_logout_request *request, im_logout_response **response)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (request == NULL || response == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
*response = util_common_calloc_s(sizeof(im_logout_response));
|
||
|
|
if (*response == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->server == NULL) {
|
||
|
|
ERROR("Logout requires server address");
|
||
|
|
lcrd_set_error_message("Logout requires server address");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->type == NULL) {
|
||
|
|
ERROR("Logout requires image type");
|
||
|
|
lcrd_set_error_message("Logout requires image type");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(request->type, NULL, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim, image type:%s", request->type);
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: logouting}", request->server);
|
||
|
|
|
||
|
|
ret = bim->ops->logout(request);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to logout %s", request->server);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: logouted}", request->server);
|
||
|
|
|
||
|
|
pack_response:
|
||
|
|
if (g_lcrd_errmsg != NULL) {
|
||
|
|
(*response)->errmsg = util_strdup_s(g_lcrd_errmsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_logout_request(im_logout_request *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->type);
|
||
|
|
ptr->type = NULL;
|
||
|
|
|
||
|
|
free(ptr->server);
|
||
|
|
ptr->server = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_logout_response(im_logout_response *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->errmsg);
|
||
|
|
ptr->errmsg = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_rm_image(im_remove_request *request, im_remove_response **response)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
char *image_ref = NULL;
|
||
|
|
const struct bim_type *bim_type = NULL;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (request == NULL || response == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
*response = util_common_calloc_s(sizeof(im_remove_response));
|
||
|
|
if (*response == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->image.image == NULL) {
|
||
|
|
ERROR("remove image requires image ref");
|
||
|
|
lcrd_set_error_message("remove image requires image ref");
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
image_ref = util_strdup_s(request->image.image);
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: removing}", image_ref);
|
||
|
|
|
||
|
|
bim_type = bim_query(image_ref);
|
||
|
|
if (bim_type == NULL) {
|
||
|
|
ERROR("No such image:%s", image_ref);
|
||
|
|
lcrd_set_error_message("No such image:%s", image_ref);
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(bim_type->image_type, image_ref, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim for image %s", image_ref);
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->rm_image(request);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to remove image %s", image_ref);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: removed}", image_ref);
|
||
|
|
|
||
|
|
pack_response:
|
||
|
|
if (g_lcrd_errmsg != NULL) {
|
||
|
|
(*response)->errmsg = util_strdup_s(g_lcrd_errmsg);
|
||
|
|
}
|
||
|
|
free(image_ref);
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_remove_request(im_remove_request *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
free(ptr->image.image);
|
||
|
|
ptr->image.image = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_remove_response(im_remove_response *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->errmsg);
|
||
|
|
ptr->errmsg = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
int im_inspect_image(const im_inspect_request *request, im_inspect_response **response)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
char *image_ref = NULL;
|
||
|
|
char *inspected_json = NULL;
|
||
|
|
const struct bim_type *bim_type = NULL;
|
||
|
|
struct bim *bim = NULL;
|
||
|
|
|
||
|
|
if (request == NULL || response == NULL) {
|
||
|
|
ERROR("Invalid input arguments");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
*response = util_common_calloc_s(sizeof(im_inspect_response));
|
||
|
|
if (*response == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (request->image.image == NULL) {
|
||
|
|
ERROR("inspect image requires image ref");
|
||
|
|
lcrd_set_error_message("inspect image requires image ref");
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
image_ref = util_strdup_s(request->image.image);
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: inspecting}", image_ref);
|
||
|
|
|
||
|
|
bim_type = bim_query(image_ref);
|
||
|
|
if (bim_type == NULL) {
|
||
|
|
ERROR("No such image:%s", image_ref);
|
||
|
|
lcrd_set_error_message("No such image:%s", image_ref);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
bim = bim_get(bim_type->image_type, image_ref, NULL, NULL);
|
||
|
|
if (bim == NULL) {
|
||
|
|
ERROR("Failed to init bim for image %s", image_ref);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = bim->ops->inspect_image(bim, &inspected_json);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to inspect image %s", image_ref);
|
||
|
|
ret = -1;
|
||
|
|
goto pack_response;
|
||
|
|
}
|
||
|
|
|
||
|
|
EVENT("Event: {Object: %s, Type: inspected}", image_ref);
|
||
|
|
|
||
|
|
pack_response:
|
||
|
|
if (g_lcrd_errmsg != NULL) {
|
||
|
|
(*response)->errmsg = util_strdup_s(g_lcrd_errmsg);
|
||
|
|
}
|
||
|
|
if (inspected_json != NULL) {
|
||
|
|
(*response)->im_inspect_json = util_strdup_s(inspected_json);
|
||
|
|
}
|
||
|
|
free(image_ref);
|
||
|
|
free(inspected_json);
|
||
|
|
bim_put(bim);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_inspect_request(im_inspect_request *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
free(ptr->image.image);
|
||
|
|
ptr->image.image = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void free_im_inspect_response(im_inspect_response *ptr)
|
||
|
|
{
|
||
|
|
if (ptr == NULL) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(ptr->im_inspect_json);
|
||
|
|
ptr->im_inspect_json = NULL;
|
||
|
|
|
||
|
|
free(ptr->errmsg);
|
||
|
|
ptr->errmsg = NULL;
|
||
|
|
|
||
|
|
free(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int bims_init(const char *rootpath)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
size_t i;
|
||
|
|
|
||
|
|
for (i = 0; i < g_numbims; i++) {
|
||
|
|
ret = g_bims[i].ops->init(rootpath);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to init bim %s", g_bims[i].image_type);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int image_module_init(const char *rootpath)
|
||
|
|
{
|
||
|
|
return bims_init(rootpath);
|
||
|
|
}
|
||
|
|
|
||
|
|
int map_to_key_value_string(const json_map_string_string *map, char ***array, size_t *array_len)
|
||
|
|
{
|
||
|
|
char **strings = NULL;
|
||
|
|
size_t strings_len = 0;
|
||
|
|
size_t i;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
if (map == NULL) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
for (i = 0; i < map->len; i++) {
|
||
|
|
char *str = NULL;
|
||
|
|
size_t len;
|
||
|
|
if (strlen(map->keys[i]) > (SIZE_MAX - strlen(map->values[i])) - 2) {
|
||
|
|
ERROR("Invalid keys/values");
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
len = strlen(map->keys[i]) + strlen(map->values[i]) + 2;
|
||
|
|
str = util_common_calloc_s(len);
|
||
|
|
if (str == NULL) {
|
||
|
|
ERROR("Out of memory");
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
ret = sprintf_s(str, len, "%s=%s", map->keys[i], map->values[i]);
|
||
|
|
if (ret < 0) {
|
||
|
|
ERROR("Failed to print string");
|
||
|
|
free(str);
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
ret = util_array_append(&strings, str);
|
||
|
|
free(str);
|
||
|
|
if (ret != 0) {
|
||
|
|
ERROR("Failed to append array");
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
strings_len++;
|
||
|
|
}
|
||
|
|
*array = strings;
|
||
|
|
*array_len = strings_len;
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
cleanup:
|
||
|
|
util_free_array(strings);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|