VPATH = @srcdir@
SOURCES=\
+ activate/activate-lv.c \
activate/activate.c \
activate/fs.c \
+ activate/ll-activate.c \
activate/names.c \
config/config.c \
datastruct/bitset.c \
--- /dev/null
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ */
+
+#include "ll-activate.h"
+#include "lvm-string.h"
+#include "log.h"
+
+#include <libdevmapper.h>
+#include <linux/kdev_t.h>
+
+
+/*
+ * Emit a target for a given segment.
+ * FIXME: tidy this function.
+ */
+static int _emit_target(struct dm_task *dmt, struct stripe_segment *seg)
+{
+ char params[1024];
+ uint64_t esize = seg->lv->vg->extent_size;
+ uint32_t s, stripes = seg->stripes;
+ int w = 0, tw = 0, error = 0;
+ const char *no_space =
+ "Insufficient space to write target parameters.";
+ char *filler = "/dev/ioerror";
+ char *target;
+
+ if (stripes == 1) {
+ if (!seg->area[0].pv) {
+ target = "error";
+ error = 1;
+ }
+ else
+ target = "linear";
+ }
+
+ if (stripes > 1) {
+ target = "striped";
+ tw = lvm_snprintf(params, sizeof(params), "%u %u ",
+ stripes, seg->stripe_size);
+
+ if (tw < 0) {
+ log_err(no_space);
+ return 0;
+ }
+
+ w = tw;
+ }
+
+ if (!error) {
+ for (s = 0; s < stripes; s++, w += tw) {
+ if (!seg->area[s].pv)
+ tw = lvm_snprintf(
+ params + w, sizeof(params) - w,
+ "%s 0%s", filler,
+ s == (stripes - 1) ? "" : " ");
+ else
+ tw = lvm_snprintf(
+ params + w, sizeof(params) - w,
+ "%s %" PRIu64 "%s",
+ dev_name(seg->area[s].pv->dev),
+ (seg->area[s].pv->pe_start +
+ (esize * seg->area[s].pe)),
+ s == (stripes - 1) ? "" : " ");
+
+ if (tw < 0) {
+ log_err(no_space);
+ return 0;
+ }
+ }
+ }
+
+ log_very_verbose("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
+ esize * seg->le, esize * seg->len,
+ target, params);
+
+ if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len,
+ target, params)) {
+ stack;
+ return 0;
+ }
+
+ return 1;
+}
+
+int _load(const char *name, struct logical_volume *lv, int task)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct list *segh;
+ struct stripe_segment *seg;
+
+ log_very_verbose("Generating devmapper parameters for %s", lv->name);
+ if (!(dmt = setup_dm_task(name, task))) {
+ stack;
+ return 0;
+ }
+
+ list_iterate(segh, &lv->segments) {
+ seg = list_item(segh, struct stripe_segment);
+ if (!_emit_target(dmt, seg)) {
+ log_error("Unable to activate logical volume '%s'",
+ lv->name);
+ goto out;
+ }
+ }
+
+ if (!((lv->status & LVM_WRITE) && (lv->vg->status & LVM_WRITE))) {
+ if (!dm_task_set_ro(dmt)) {
+ log_error("Failed to set %s read-only during "
+ "activation.", lv->name);
+ goto out;
+ } else
+ log_very_verbose("Activating %s read-only", lv->name);
+ }
+
+ if (lv->minor >= 0) {
+ if (!dm_task_set_minor(dmt, MINOR(lv->minor))) {
+ log_error("Failed to set minor number for %s to %d "
+ "during activation.", lv->name, lv->minor);
+ goto out;
+ } else
+ log_very_verbose("Set minor number for %s to %d.",
+ lv->name, lv->minor);
+ }
+
+ if (!(r = dm_task_run(dmt)))
+ stack;
+
+ log_verbose("Logical volume %s%s activated", lv->name,
+ r == 1 ? "" : " not");
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+int device_create_lv(const char *name, struct logical_volume *lv, int minor)
+{
+ log_very_verbose("Activating %s", name);
+ return _load(name, lv, DM_DEVICE_CREATE);
+}
+
+int device_reload_lv(const char *name, struct logical_volume *lv)
+{
+ log_very_verbose("Reactivating %s", name);
+ return _load(name, lv, DM_DEVICE_RELOAD);
+}
#include "metadata.h"
#include "activate.h"
+#include "ll-activate.h"
#include "display.h"
#include "log.h"
#include "fs.h"
#include "names.h"
#include <limits.h>
-#include <linux/kdev_t.h>
+#define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args)
int library_version(char *version, size_t size)
{
return 1;
}
-static struct dm_task *_setup_task_with_name(struct logical_volume *lv,
- const char *lv_name,
- int task)
-{
- char name[128];
- struct dm_task *dmt;
-
- if (!(dmt = dm_task_create(task))) {
- stack;
- return NULL;
- }
-
- if (!build_dm_name(name, sizeof(name), lv->vg->name, lv_name)) {
- stack;
- return NULL;
- }
-
- dm_task_set_name(dmt, name);
-
- return dmt;
-}
-
-static struct dm_task *_setup_task(struct logical_volume *lv, int task)
-{
- return _setup_task_with_name(lv, lv->name, task);
-}
-
int driver_version(char *version, size_t size)
{
int r = 0;
return r;
}
-int lv_info(struct logical_volume *lv, struct dm_info *info)
+static int _query(struct logical_volume *lv, int (*fn)(const char *))
{
- int r = 0;
- struct dm_task *dmt;
-
- log_very_verbose("Getting device info for %s", lv->name);
- if (!(dmt = _setup_task(lv, DM_DEVICE_INFO))) {
- stack;
- return 0;
- }
-
- if (!dm_task_run(dmt)) {
- stack;
- goto out;
- }
-
- if (!dm_task_get_info(dmt, info)) {
- stack;
- goto out;
- }
- r = 1;
-
- out:
- dm_task_destroy(dmt);
- return r;
-}
-
-int lv_rename(const char *old_name, struct logical_volume *lv)
-{
- int r = 0;
- char new_name[PATH_MAX];
- struct dm_task *dmt;
-
- if (test_mode())
- return 0;
+ char buffer[128];
- if (!(dmt = _setup_task_with_name(lv, old_name, DM_DEVICE_RENAME))) {
- stack;
- return 0;
- }
-
- if (!build_dm_name(new_name, sizeof(new_name),
+ if (!build_dm_name(buffer, sizeof(buffer), "",
lv->vg->name, lv->name)) {
stack;
- return 0;
+ return -1;
}
- if (!dm_task_set_newname(dmt, new_name)) {
- stack;
- r = 0;
- goto end;
- }
-
- if (!dm_task_run(dmt)) {
- stack;
- r = 0;
- goto end;
- }
-
- fs_rename_lv(old_name, lv);
-
- end:
- dm_task_destroy(dmt);
- return r;
+ return fn(buffer);
}
int lv_active(struct logical_volume *lv)
{
- int r = -1;
- struct dm_info info;
-
- if (!lv_info(lv, &info)) {
- stack;
- return r;
- }
-
- log_very_verbose("%s is%s active", lv->name, info.exists ? "":" not");
- return info.exists;
+ return _query(lv, device_active);
}
int lv_suspended(struct logical_volume *lv)
{
- int r = -1;
- struct dm_info info;
-
- if (!lv_info(lv, &info)) {
- stack;
- return r;
- }
-
- log_very_verbose("%s is%s suspended", lv->name,
- info.suspended ? "":" not");
- return info.suspended;
+ return _query(lv, device_suspended);
}
int lv_open_count(struct logical_volume *lv)
{
- int r = -1;
- struct dm_info info;
+ return _query(lv, device_open_count);
+}
+
+int lv_info(struct logical_volume *lv, struct dm_info *info)
+{
+ char buffer[128];
- if (!lv_info(lv, &info)) {
+ if (!build_dm_name(buffer, sizeof(buffer), "",
+ lv->vg->name, lv->name)) {
stack;
- return r;
+ return -1;
}
- log_very_verbose("%s is open %d time(s)", lv->name, info.open_count);
- return info.open_count;
+ return device_info(buffer, info);
}
-/*
- * Emit a target for a given segment.
- */
-static int _emit_target(struct dm_task *dmt, struct stripe_segment *seg)
-{
- char params[1024];
- uint64_t esize = seg->lv->vg->extent_size;
- uint32_t s, stripes = seg->stripes;
- int w = 0, tw = 0, error = 0;
- const char *no_space =
- "Insufficient space to write target parameters.";
- char *filler = "/dev/ioerror";
- char *target;
-
- if (stripes == 1) {
- if (!seg->area[0].pv) {
- target = "error";
- error = 1;
- }
- else
- target = "linear";
- }
-
- if (stripes > 1) {
- target = "striped";
- tw = lvm_snprintf(params, sizeof(params), "%u %u ",
- stripes, seg->stripe_size);
- if (tw < 0) {
- log_err(no_space);
- return 0;
- }
+int lv_activate(struct logical_volume *lv)
+{
+ char buffer[128];
- w = tw;
+ if (test_mode()) {
+ _skip("Activation of '%s'.", lv->name);
+ return 0;
}
- if (!error) {
- for (s = 0; s < stripes; s++, w += tw) {
- if (!seg->area[s].pv)
- tw = lvm_snprintf(
- params + w, sizeof(params) - w,
- "%s 0%s", filler,
- s == (stripes - 1) ? "" : " ");
- else
- tw = lvm_snprintf(
- params + w, sizeof(params) - w,
- "%s %" PRIu64 "%s",
- dev_name(seg->area[s].pv->dev),
- (seg->area[s].pv->pe_start +
- (esize * seg->area[s].pe)),
- s == (stripes - 1) ? "" : " ");
-
- if (tw < 0) {
- log_err(no_space);
- return 0;
- }
- }
+ if (!build_dm_name(buffer, sizeof(buffer), "",
+ lv->vg->name, lv->name)) {
+ stack;
+ return 0;
}
- log_very_verbose("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
- esize * seg->le, esize * seg->len,
- target, params);
+ if (!device_create_lv(buffer, lv, lv->minor)) {
+ stack;
+ return 0;
+ }
- if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len,
- target, params)) {
+ if (!fs_add_lv(lv, lv->minor)) {
stack;
return 0;
}
return 1;
}
-int _load(struct logical_volume *lv, int task)
+int lv_reactivate(struct logical_volume *lv)
{
- int r = 0;
- struct dm_task *dmt;
- struct list *segh;
- struct stripe_segment *seg;
+ int r;
+ char buffer[128];
- log_very_verbose("Generating devmapper parameters for %s", lv->name);
- if (!(dmt = _setup_task(lv, task))) {
- stack;
+ if (test_mode()) {
+ _skip("Reactivation of '%s'.", lv->name);
return 0;
}
- list_iterate(segh, &lv->segments) {
- seg = list_item(segh, struct stripe_segment);
- if (!_emit_target(dmt, seg)) {
- log_error("Unable to activate logical volume '%s'",
- lv->name);
- goto out;
- }
+ if (!build_dm_name(buffer, sizeof(buffer), "",
+ lv->vg->name, lv->name)) {
+ stack;
+ return 0;
}
- if (!((lv->status & LVM_WRITE) && (lv->vg->status & LVM_WRITE))) {
- if (!dm_task_set_ro(dmt)) {
- log_error("Failed to set %s read-only during "
- "activation.", lv->name);
- goto out;
- } else
- log_very_verbose("Activating %s read-only", lv->name);
+ if (!device_suspended(buffer) && !device_suspend(buffer)) {
+ stack;
+ return 0;
}
- if (lv->minor >= 0) {
- if (!dm_task_set_minor(dmt, MINOR(lv->minor))) {
- log_error("Failed to set minor number for %s to %d "
- "during activation.", lv->name, lv->minor);
- goto out;
- } else
- log_very_verbose("Set minor number for %s to %d.",
- lv->name, lv->minor);
- }
+ r = device_reload_lv(buffer, lv);
- if (!(r = dm_task_run(dmt)))
+ if (!device_resume(buffer)) {
stack;
+ return 0;
+ }
- log_verbose("Logical volume %s%s activated", lv->name,
- r == 1 ? "" : " not");
-
- out:
- dm_task_destroy(dmt);
return r;
}
-/* FIXME: Always display error msg */
-int lv_activate(struct logical_volume *lv)
+int lv_deactivate(struct logical_volume *lv)
{
- if (test_mode())
- return 0;
+ char buffer[128];
- log_very_verbose("Activating %s", lv->name);
- return _load(lv, DM_DEVICE_CREATE) && fs_add_lv(lv);
-}
+ log_very_verbose("Deactivating %s", lv->name);
+ if (test_mode()) {
+ _skip("Deactivating '%s'.", lv->name);
+ return 0;
+ }
-int _suspend(struct logical_volume *lv, int sus)
-{
- int r;
- struct dm_task *dmt;
- int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME;
+ if (!build_dm_name(buffer, sizeof(buffer), "",
+ lv->vg->name, lv->name)) {
+ stack;
+ return 0;
+ }
- log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", lv->name);
- if (!(dmt = _setup_task(lv, task))) {
+ if (!device_deactivate(buffer)) {
stack;
return 0;
}
- if (!(r = dm_task_run(dmt)))
- log_err("Couldn't %s device '%s'", sus ? "suspend" : "resume",
- lv->name);
+ fs_del_lv(lv);
- dm_task_destroy(dmt);
- return r;
+ return 1;
}
int lv_suspend(struct logical_volume *lv)
{
- return _suspend(lv, 1);
-}
+ char buffer[128];
-int lv_reactivate(struct logical_volume *lv)
-{
- int r;
-
- if (test_mode())
+ log_very_verbose("Suspending %s", lv->name);
+ if (test_mode()) {
+ _skip("Suspending '%s'.", lv->name);
return 0;
+ }
- if (!lv_suspended(lv) && !_suspend(lv, 1)) {
+ if (!build_dm_name(buffer, sizeof(buffer), "",
+ lv->vg->name, lv->name)) {
stack;
return 0;
}
- r = _load(lv, DM_DEVICE_RELOAD);
-
- if (!_suspend(lv, 0)) {
+ if (!device_suspend(buffer)) {
stack;
return 0;
}
- return r;
-}
+ fs_del_lv(lv);
+ return 1;
+}
-int lv_deactivate(struct logical_volume *lv)
+int lv_rename(const char *old_name, struct logical_volume *lv)
{
- int r;
+ int r = 0;
+ char new_name[PATH_MAX];
struct dm_task *dmt;
- log_very_verbose("Deactivating %s", lv->name);
- if (test_mode())
+ if (test_mode()) {
+ _skip("Rename '%s' to '%s'.", old_name, lv->name);
return 0;
+ }
- if (!(dmt = _setup_task(lv, DM_DEVICE_REMOVE))) {
+ if (!(dmt = setup_dm_task(old_name, DM_DEVICE_RENAME))) {
stack;
return 0;
}
- if (!(r = dm_task_run(dmt)))
+ if (!build_dm_name(new_name, sizeof(new_name), "",
+ lv->vg->name, lv->name)) {
stack;
+ return 0;
+ }
- dm_task_destroy(dmt);
+ if (!dm_task_set_newname(dmt, new_name)) {
+ stack;
+ r = 0;
+ goto end;
+ }
- fs_del_lv(lv);
+ if (!dm_task_run(dmt)) {
+ stack;
+ r = 0;
+ goto end;
+ }
+
+ fs_rename_lv(old_name, lv);
+ end:
+ dm_task_destroy(dmt);
return r;
}
+
int activate_lvs_in_vg(struct volume_group *vg)
{
struct list *lvh;
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
- * This file is released under the GPL.
+ * This file is released under the LGPL.
*/
#ifndef LVM_ACTIVATE_H
#include <libdevmapper.h>
-/* FIXME Snapshot handling? */
-
int driver_version(char *version, size_t size);
int library_version(char *version, size_t size);
+/*
+ * Status functions.
+ */
int lv_active(struct logical_volume *lv);
int lv_suspended(struct logical_volume *lv);
int lv_open_count(struct logical_volume *lv);
int lv_info(struct logical_volume *lv, struct dm_info *info);
-int lv_rename(const char *old_name, struct logical_volume *lv);
+/*
+ * Activation proper.
+ */
int lv_activate(struct logical_volume *lv);
int lv_reactivate(struct logical_volume *lv);
int lv_deactivate(struct logical_volume *lv);
int lv_suspend(struct logical_volume *lv);
+int lv_rename(const char *old_name, struct logical_volume *lv);
/*
- * Return number of LVs in the VG that are
- * active.
+ * Return number of LVs in the VG that are active.
*/
int lvs_in_vg_activated(struct volume_group *vg);
int lvs_in_vg_opened(struct volume_group *vg);
int lv_update_write_access(struct logical_volume *lv);
/*
- * Activate all LVs in the VG. Ignore any that
- * are already active. Return number
- * activated.
+ * Activate all LVs in the VG. Ignore any that are already
+ * active. Return number activated.
*/
int activate_lvs_in_vg(struct volume_group *vg);
char lv_path[PATH_MAX], link_path[PATH_MAX];
struct stat buf;
- if (!build_dm_path(lv_path, sizeof(lv_path), lv->vg->name, lv->name)) {
+ if (!build_dm_path(lv_path, sizeof(lv_path), "",
+ lv->vg->name, lv->name)) {
log_error("Couldn't create destination pathname for "
"logical volume link for %s", lv->name);
return 0;
return 1;
}
-int fs_add_lv(struct logical_volume *lv)
+int fs_add_lv(struct logical_volume *lv, int minor)
{
if (!_mk_dir(lv->vg) ||
!_mk_link(lv)) {
* symbolic links to the dm device.
*/
-int fs_add_lv(struct logical_volume *lv);
+int fs_add_lv(struct logical_volume *lv, int minor);
int fs_del_lv(struct logical_volume *lv);
int fs_rename_lv(const char *old_name, struct logical_volume *lv);
--- /dev/null
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ */
+
+#include "ll-activate.h"
+#include "log.h"
+
+struct dm_task *setup_dm_task(const char *name, int task)
+{
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(task))) {
+ stack;
+ return NULL;
+ }
+
+ dm_task_set_name(dmt, name);
+
+ return dmt;
+}
+
+int device_info(const char *name, struct dm_info *info)
+{
+ int r = 0;
+ struct dm_task *dmt;
+
+ log_very_verbose("Getting device info for %s", name);
+ if (!(dmt = setup_dm_task(name, DM_DEVICE_INFO))) {
+ stack;
+ return 0;
+ }
+
+ if (!dm_task_run(dmt)) {
+ stack;
+ goto out;
+ }
+
+ if (!dm_task_get_info(dmt, info)) {
+ stack;
+ goto out;
+ }
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+int device_active(const char *name)
+{
+ struct dm_info info;
+
+ if (!device_info(name, &info)) {
+ stack;
+ return -1;
+ }
+
+ log_very_verbose("%s is%s active", name, info.exists ? "" : " not");
+ return info.exists;
+}
+
+int device_suspended(const char *name)
+{
+ struct dm_info info;
+
+ if (!device_info(name, &info)) {
+ stack;
+ return -1;
+ }
+
+ log_very_verbose("%s is%s suspended", name,
+ info.suspended ? "" : " not");
+ return info.suspended;
+}
+
+int device_open_count(const char *name)
+{
+ struct dm_info info;
+
+ if (!device_info(name, &info)) {
+ stack;
+ return -1;
+ }
+
+ log_very_verbose("%s is open %d time(s)", name, info.open_count);
+ return info.open_count;
+}
+
+
+int device_deactivate(const char *name)
+{
+ int r;
+ struct dm_task *dmt;
+
+ log_very_verbose("Deactivating '%s'.", name);
+
+ if (!(dmt = setup_dm_task(name, DM_DEVICE_REMOVE))) {
+ stack;
+ return 0;
+ }
+
+ if (!(r = dm_task_run(dmt)))
+ stack;
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _suspend(const char *name, int sus)
+{
+ int r;
+ struct dm_task *dmt;
+ int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME;
+
+ log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", name);
+ if (!(dmt = setup_dm_task(name, task))) {
+ stack;
+ return 0;
+ }
+
+ if (!(r = dm_task_run(dmt)))
+ log_err("Couldn't %s device '%s'", sus ? "suspend" : "resume",
+ name);
+
+ dm_task_destroy(dmt);
+ return r;
+}
+
+int device_suspend(const char *name)
+{
+ return _suspend(name, 1);
+}
+
+int device_resume(const char *name)
+{
+ return _suspend(name, 0);
+}
--- /dev/null
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef _LVM_LL_ACTIVATE_H
+#define _LVM_LL_ACTIVATE_H
+
+#include "metadata.h"
+
+#include <libdevmapper.h>
+
+/*
+ * Prepares a new dm_task.
+ */
+struct dm_task *setup_dm_task(const char *name, int task);
+
+
+/*
+ * Query functions. 'name' is the device node name in the
+ * device-mapper dir.
+ */
+int device_info(const char *name, struct dm_info *info);
+int device_active(const char *name);
+int device_suspended(const char *name);
+int device_open_count(const char *name);
+
+
+/*
+ * Functions to manipulate an already active device.
+ */
+int device_deactivate(const char *name);
+int device_suspend(const char *name);
+int device_resume(const char *name);
+
+
+/*
+ * Creates a device with a mapping table as specified by the lv.
+ */
+int device_create_lv(const char *name, struct logical_volume *lv, int minor);
+int device_reload_lv(const char *name, struct logical_volume *lv);
+
+
+/*
+ * Layers the origin device above an already active 'real'
+ * device.
+ */
+int device_create_origin(struct logical_volume *lv,
+ const char *real, int minor);
+
+
+/*
+ * Creates a snapshot device for a given origin and exception
+ * storage area.
+ */
+int device_create_snapshot(struct logical_volume *lv,
+ const char *origin, const char *cow_device,
+ int minor);
+
+#endif
* seperated by a single ':', any colons in the vg name are
* doubled up to form a pair.
*/
-int build_dm_name(char *buffer, size_t len,
+int build_dm_name(char *buffer, size_t len, const char *prefix,
const char *vg_name, const char *lv_name)
{
char *out;
if (!len)
return 0;
- if (lvm_snprintf(out, len, ":%s", lv_name) == -1) {
+ if (lvm_snprintf(out, len, ":%s%s", prefix, lv_name) == -1) {
log_err("Couldn't build logical volume name.");
return 0;
}
return 1;
}
-int build_dm_path(char *buffer, size_t len,
+int build_dm_path(char *buffer, size_t len, const char *prefix,
const char *vg_name, const char *lv_name)
{
char dev_name[PATH_MAX];
- if (!build_dm_name(dev_name, sizeof(dev_name), vg_name, lv_name)) {
+ if (!build_dm_name(dev_name, sizeof(dev_name),
+ prefix, vg_name, lv_name)) {
stack;
return 0;
}
* The name of the device-mapper device for a particular LV.
* eg, vg0:music
*/
-int build_dm_name(char *buffer, size_t len,
+int build_dm_name(char *buffer, size_t len, const char *prefix,
const char *vg_name, const char *lv_name);
/*
* The path of the device-mapper device for a particular LV.
* eg, /dev/device-mapper/vg0:music
*/
-int build_dm_path(char *buffer, size_t len,
+int build_dm_path(char *buffer, size_t len, const char *prefix,
const char *vg_name, const char *lv_name);
/*
#define log_err(x...) plog(_LOG_ERR, x)
#define log_fatal(x...) plog(_LOG_FATAL, x)
-#define stack log_debug( "s" ) /* Backtrace on error */
+#define stack log_debug("<stack>") /* Backtrace on error */
#define log_error(fmt, args...) log_err(fmt , ## args)
#define log_print(fmt, args...) log_warn(fmt , ## args)