GError*
meta1_backend_services_poll(struct meta1_backend_s *m1,
		struct oio_url_s *url, const char *srvtype,
		gboolean dryrun, gboolean autocreate,
		gchar ***result)
{
	EXTRA_ASSERT(srvtype != NULL);
	EXTRA_ASSERT(result != NULL);

	gboolean renewed = FALSE;
	struct sqlx_sqlite3_s *sq3 = NULL;
	GError *err = _open_and_lock(m1, url, M1V2_OPENBASE_MASTERONLY, &sq3);
	if (err) return err;

	struct sqlx_repctx_s *repctx = NULL;
	if (!(err = sqlx_transaction_begin(sq3, &repctx))) {
		if (!(err = __info_user(sq3, url, autocreate, NULL))) {
			enum m1v2_getsrv_e mode = dryrun ? M1V2_GETSRV_DRYRUN : M1V2_GETSRV_RENEW;
			err = __get_container_service(sq3, url, srvtype, m1, mode, result, &renewed);
			if (NULL != err)
				g_prefix_error(&err, "Query error: ");
		}
		if (!(err = sqlx_transaction_end(repctx, err)) && renewed) {
			if (renewed)
				__notify_services_by_cid(m1, sq3, url);
		}
	}

	sqlx_repository_unlock_and_close_noerror(sq3);
	return err;
}
GError*
meta1_backend_services_set(struct meta1_backend_s *m1,
		struct oio_url_s *url, const char *packedurl,
		gboolean autocreate, gboolean force)
{
	struct meta1_service_url_s *m1url;
	if (!(m1url = meta1_unpack_url(packedurl)))
		return NEWERROR(CODE_BAD_REQUEST, "Invalid URL");

	struct sqlx_sqlite3_s *sq3 = NULL;
	GError *err = _open_and_lock(m1, url, M1V2_OPENBASE_MASTERONLY, &sq3);
	if (err) { g_free(m1url); return err; }

	struct sqlx_repctx_s *repctx = NULL;
	if (!(err = sqlx_transaction_begin(sq3, &repctx))) {
		if (!(err = __info_user(sq3, url, autocreate, NULL)))
			err = __save_service(sq3, url, m1url, force);
		if (!(err = sqlx_transaction_end(repctx, err)))
			__notify_services_by_cid(m1, sq3, url);
	}

	sqlx_repository_unlock_and_close_noerror(sq3);
	g_free(m1url);

	/* XXX JFS: ugly quirk until we find a pretty way to distinguish the
	 * commit errors (e.g. it can fail because of a replication error or
	 * a constraint violation) */
	if (err && NULL != strstr(err->message, "UNIQUE"))
		err->code = CODE_SRV_ALREADY;

	return err;
}
GError*
meta1_backend_services_config(struct meta1_backend_s *m1,
		struct oio_url_s *url, const char *packedurl)
{
	struct meta1_service_url_s *m1url;
	if (!(m1url = meta1_unpack_url(packedurl)))
		return NEWERROR(CODE_BAD_REQUEST, "Invalid URL");

	GRID_DEBUG("About to reconfigure [%s] [%"G_GINT64_FORMAT"] [%s] [%s]",
			m1url->srvtype, m1url->seq, m1url->host, m1url->args);

	struct sqlx_sqlite3_s *sq3 = NULL;
	GError *err = _open_and_lock(m1, url, M1V2_OPENBASE_MASTERONLY, &sq3);
	if (err) { g_free(m1url); return err; }

	struct sqlx_repctx_s *repctx = NULL;
	if (!(err = sqlx_transaction_begin(sq3, &repctx))) {
		if (!(err = __info_user(sq3, url, FALSE, NULL)))
			err = __configure_service(sq3, url, m1url);
		if (!(err = sqlx_transaction_end(repctx, err)))
			__notify_services_by_cid(m1, sq3, url);
	}

	sqlx_repository_unlock_and_close_noerror(sq3);
	g_free(m1url);
	return err;
}
GError*
meta1_backend_services_unlink(struct meta1_backend_s *m1,
		struct oio_url_s *url, const char *srvtype, gchar **urlv)
{
	GError *err = __check_backend_events (m1);
	if (err) return err;

	EXTRA_ASSERT(srvtype != NULL);
	struct sqlx_sqlite3_s *sq3 = NULL;
	err = _open_and_lock(m1, url, M1V2_OPENBASE_MASTERONLY, &sq3);
	if (err) return err;

	struct sqlx_repctx_s *repctx = NULL;
	if (!(err = sqlx_transaction_begin(sq3, &repctx))) {
		if (!(err = __info_user(sq3, url, FALSE, NULL))) {
			err = __del_container_services(sq3, url, srvtype, urlv);
			if (NULL != err)
				g_prefix_error(&err, "Query error: ");
		}
		if (!(err = sqlx_transaction_end(repctx, err)))
			__notify_services_by_cid(m1, sq3, url);
	}

	sqlx_repository_unlock_and_close_noerror(sq3);
	return err;
}
GError *
meta1_backend_notify_services(struct meta1_backend_s *m1, struct oio_url_s *url)
{
    struct sqlx_sqlite3_s *sq3 = NULL;
    GError *err = _open_and_lock(m1, url, M1V2_OPENBASE_MASTERSLAVE, &sq3);
    if (!err) {
        __notify_services_by_cid(m1, sq3, url);
        sqlx_repository_unlock_and_close_noerror(sq3);
    }
    return err;
}
GError*
meta1_backend_services_relink(struct meta1_backend_s *m1,
                              struct oio_url_s *url, const char *kept, const char *replaced,
                              gboolean dryrun, gchar ***out)
{
    GError *err = NULL;
    struct meta1_service_url_s **ukept = NULL, **urepl = NULL;
    /* fields to be prefetched */
    struct grid_lb_iterator_s *iterator = NULL;
    struct compound_type_s ct;

    memset (&ct, 0, sizeof(ct));
    ukept = __parse_and_expand (kept);
    urepl = __parse_and_expand (replaced);

    /* Sanity checks: we must receive at least one service */
    if ((!ukept || !*ukept) && (!urepl || !*urepl)) {
        err = NEWERROR (CODE_BAD_REQUEST, "Missing URL set");
        goto out;
    }
    /* Sanity check : all the services must have the same <seq,type> */
    struct meta1_service_url_s *ref = ukept && *ukept ? *ukept : *urepl;
    for (struct meta1_service_url_s **p = ukept; p && *p ; ++p) {
        if (0 != _sorter(p, &ref)) {
            err = NEWERROR(CODE_BAD_REQUEST, "Mismatch in URL set (%s)", "kept");
            goto out;
        }
    }
    for (struct meta1_service_url_s **p = urepl; p && *p ; ++p) {
        if (0 != _sorter(p, &ref)) {
            err = NEWERROR(CODE_BAD_REQUEST, "Mismatch in URL set (%s)", "kept");
            goto out;
        }
    }

    /* prefetch some fields from the backend: the compound type (so it is
     * parsed only once), the iterator (so we can already poll services, out
     * of the sqlite3 transaction) */
    if (NULL != (err = compound_type_parse(&ct, ref->srvtype))) {
        err = NEWERROR(CODE_BAD_REQUEST, "Invalid service type");
        goto out;
    }
    if (NULL != (err = _get_iterator (m1, &ct, &iterator))) {
        err = NEWERROR(CODE_BAD_REQUEST, "Service type not managed");
        goto out;
    }

    /* Call the backend logic now */
    struct sqlx_sqlite3_s *sq3 = NULL;
    struct sqlx_repctx_s *repctx = NULL;
    if (!(err = _open_and_lock(m1, url, M1V2_OPENBASE_MASTERONLY, &sq3))) {
        if (!(err = sqlx_transaction_begin(sq3, &repctx))) {
            if (!(err = __info_user(sq3, url, FALSE, NULL))) {
                struct m1v2_relink_input_s in = {
                    .m1 = m1, .sq3 = sq3, .url = url,
                    .iterator = iterator, .ct = &ct,
                    .kept = ukept, .replaced = urepl,
                    .dryrun = dryrun
                };
                err = __relink_container_services(&in, out);
            }
            if (!(err = sqlx_transaction_end(repctx, err))) {
                if (!dryrun)
                    __notify_services_by_cid(m1, sq3, url);
            }
        }
        sqlx_repository_unlock_and_close_noerror(sq3);
    }

out:
    meta1_service_url_cleanv (ukept);
    meta1_service_url_cleanv (urepl);
    grid_lb_iterator_clean (iterator);
    compound_type_clean (&ct);
    return err;
}