mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-23 21:05:44 +03:00
remove csync_lock
It is not used anymore for a long time
This commit is contained in:
parent
49562ca3b6
commit
ee3df45fd8
8 changed files with 5 additions and 455 deletions
|
@ -61,7 +61,6 @@ set(csync_SRCS
|
|||
csync_time.c
|
||||
csync_util.c
|
||||
csync_misc.c
|
||||
csync_lock.c
|
||||
|
||||
csync_update.c
|
||||
csync_reconcile.c
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include "c_lib.h"
|
||||
#include "csync_private.h"
|
||||
#include "csync_exclude.h"
|
||||
#include "csync_lock.h"
|
||||
#include "csync_statedb.h"
|
||||
#include "csync_time.h"
|
||||
#include "csync_util.h"
|
||||
|
@ -168,7 +167,6 @@ int csync_create(CSYNC **csync, const char *local, const char *remote) {
|
|||
int csync_init(CSYNC *ctx) {
|
||||
int rc;
|
||||
time_t timediff = -1;
|
||||
char *lock = NULL;
|
||||
char *config = NULL;
|
||||
|
||||
if (ctx == NULL) {
|
||||
|
@ -183,19 +181,6 @@ int csync_init(CSYNC *ctx) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* create lock file */
|
||||
if (asprintf(&lock, "%s/%s", ctx->local.uri, CSYNC_LOCK_FILE) < 0) {
|
||||
rc = -1;
|
||||
ctx->status_code = CSYNC_STATUS_MEMORY_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (csync_lock(lock) < 0) {
|
||||
rc = -1;
|
||||
ctx->status_code = CSYNC_STATUS_NO_LOCK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->local.type = LOCAL_REPLICA;
|
||||
|
||||
/* check for uri */
|
||||
|
@ -286,8 +271,6 @@ retry_vio_init:
|
|||
|
||||
ctx->status = CSYNC_STATUS_INIT;
|
||||
|
||||
csync_lock_remove(lock);
|
||||
|
||||
csync_set_module_property(ctx, "csync_context", ctx);
|
||||
|
||||
/* initialize random generator */
|
||||
|
@ -296,7 +279,6 @@ retry_vio_init:
|
|||
rc = 0;
|
||||
|
||||
out:
|
||||
SAFE_FREE(lock);
|
||||
SAFE_FREE(config);
|
||||
return rc;
|
||||
}
|
||||
|
@ -304,7 +286,6 @@ out:
|
|||
int csync_update(CSYNC *ctx) {
|
||||
int rc = -1;
|
||||
struct timespec start, finish;
|
||||
char *lock = NULL;
|
||||
|
||||
if (ctx == NULL) {
|
||||
errno = EBADF;
|
||||
|
@ -312,21 +293,6 @@ int csync_update(CSYNC *ctx) {
|
|||
}
|
||||
ctx->status_code = CSYNC_STATUS_OK;
|
||||
|
||||
/* try to create lock file */
|
||||
if (asprintf(&lock, "%s/%s", ctx->local.uri, CSYNC_LOCK_FILE) < 0) {
|
||||
ctx->status_code = CSYNC_STATUS_MEMORY_ERROR;
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (csync_lock(lock) < 0) {
|
||||
ctx->status_code = CSYNC_STATUS_NO_LOCK;
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
SAFE_FREE(lock);
|
||||
|
||||
/* create/load statedb */
|
||||
if (! csync_is_statedb_disabled(ctx)) {
|
||||
rc = asprintf(&ctx->statedb.file, "%s/.csync_journal.db",
|
||||
|
@ -698,8 +664,7 @@ out:
|
|||
}
|
||||
|
||||
int csync_destroy(CSYNC *ctx) {
|
||||
char *lock = NULL;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
if (ctx == NULL) {
|
||||
errno = EBADF;
|
||||
|
@ -719,12 +684,6 @@ int csync_destroy(CSYNC *ctx) {
|
|||
/* destroy exclude list */
|
||||
csync_exclude_destroy(ctx);
|
||||
|
||||
/* remove the lock file */
|
||||
rc = asprintf(&lock, "%s/%s", ctx->options.config_dir, CSYNC_LOCK_FILE);
|
||||
if (rc > 0) {
|
||||
csync_lock_remove(lock);
|
||||
}
|
||||
|
||||
_csync_clean_ctx(ctx);
|
||||
|
||||
SAFE_FREE(ctx->local.uri);
|
||||
|
@ -738,9 +697,7 @@ int csync_destroy(CSYNC *ctx) {
|
|||
|
||||
SAFE_FREE(ctx);
|
||||
|
||||
SAFE_FREE(lock);
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check if csync is the required version or get the version string. */
|
||||
|
|
|
@ -50,7 +50,6 @@ extern "C" {
|
|||
#define CSYNC_CONF_DIR ".ocsync"
|
||||
#define CSYNC_CONF_FILE "ocsync.conf"
|
||||
#define CSYNC_EXCLUDE_FILE "ocsync_exclude.conf"
|
||||
#define CSYNC_LOCK_FILE ".csync.lock"
|
||||
|
||||
/**
|
||||
* Instruction enum. In the file traversal structure, it describes
|
||||
|
@ -62,7 +61,7 @@ enum csync_status_codes_e {
|
|||
CSYNC_STATUS_ERROR = 1024, /* don't use this code,
|
||||
just use in csync_status_ok */
|
||||
CSYNC_STATUS_UNSUCCESSFUL,
|
||||
CSYNC_STATUS_NO_LOCK,
|
||||
CSYNC_STATUS_NO_LOCK, /* OBSOLETE does not happen anymore */
|
||||
CSYNC_STATUS_STATEDB_LOAD_ERROR,
|
||||
CSYNC_STATUS_STATEDB_WRITE_ERROR,
|
||||
CSYNC_STATUS_NO_MODULE,
|
||||
|
@ -242,7 +241,7 @@ int csync_create(CSYNC **csync, const char *local, const char *remote);
|
|||
/**
|
||||
* @brief Initialize the file synchronizer.
|
||||
*
|
||||
* This function loads the configuration, the statedb and locks the client.
|
||||
* This function loads the configuration
|
||||
*
|
||||
* @param ctx The context to initialize.
|
||||
*
|
||||
|
@ -289,7 +288,7 @@ int csync_commit(CSYNC *ctx);
|
|||
/**
|
||||
* @brief Destroy the csync context
|
||||
*
|
||||
* Writes the statedb, unlocks csync and frees the memory.
|
||||
* frees the memory.
|
||||
*
|
||||
* @param ctx The context to destroy.
|
||||
*
|
||||
|
|
|
@ -148,11 +148,6 @@ CSYNC_EXCLUDE_TYPE csync_excluded(CSYNC *ctx, const char *path, int filetype) {
|
|||
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
|
||||
CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED;
|
||||
|
||||
/* exclude the lock file */
|
||||
if (c_streq( path, CSYNC_LOCK_FILE )) {
|
||||
return CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||
}
|
||||
|
||||
if (! ctx->options.unix_extensions) {
|
||||
for (p = path; *p; p++) {
|
||||
switch (*p) {
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config_csync.h"
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "c_lib.h"
|
||||
#include "csync_lock.h"
|
||||
#include "csync.h"
|
||||
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.lock"
|
||||
#include "csync_log.h"
|
||||
|
||||
#ifdef _DO_CREATE_A_LOCK_FILE
|
||||
static int _csync_lock_create(const char *lockfile) {
|
||||
int fd = -1;
|
||||
pid_t pid = 0;
|
||||
int rc = -1;
|
||||
char errbuf[256] = {0};
|
||||
char *ctmpfile = NULL;
|
||||
char *dir = NULL;
|
||||
char *buf = NULL;
|
||||
mode_t mask;
|
||||
|
||||
pid = getpid();
|
||||
|
||||
dir = c_dirname(lockfile);
|
||||
if (dir == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (asprintf(&ctmpfile, "%s/tmp_lock_XXXXXX", dir) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Create temporary lock file: %s", ctmpfile);
|
||||
mask = umask(0077);
|
||||
fd = mkstemp(ctmpfile);
|
||||
umask(mask);
|
||||
if (fd < 0) {
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
|
||||
"Unable to create temporary lock file: %s - %s",
|
||||
ctmpfile,
|
||||
errbuf);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Write pid (%d) to temporary lock file: %s", pid, ctmpfile);
|
||||
pid = asprintf(&buf, "%d\n", pid);
|
||||
if (write(fd, buf, pid) == pid) {
|
||||
/* Create lock file */
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Create a hardlink from %s to %s.", ctmpfile, lockfile);
|
||||
if (link(ctmpfile, lockfile) < 0 ) {
|
||||
/* Oops, alredy locked */
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO,
|
||||
"Already locked: %s - %s",
|
||||
lockfile,
|
||||
errbuf);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
|
||||
"Can't create %s - %s",
|
||||
ctmpfile,
|
||||
errbuf);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (fd > 0) {
|
||||
close(fd);
|
||||
}
|
||||
if (ctmpfile) {
|
||||
unlink(ctmpfile);
|
||||
}
|
||||
|
||||
SAFE_FREE(buf);
|
||||
SAFE_FREE(dir);
|
||||
SAFE_FREE(ctmpfile);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static pid_t _csync_lock_read(const char *lockfile) {
|
||||
char errbuf[256] = {0};
|
||||
char buf[8] = {0};
|
||||
long int tmp;
|
||||
ssize_t rc;
|
||||
int fd;
|
||||
pid_t pid;
|
||||
mbchar_t *wlockfile;
|
||||
|
||||
/* Read PID from existing lock */
|
||||
#ifdef _WIN32
|
||||
_fmode = _O_BINARY;
|
||||
#endif
|
||||
|
||||
wlockfile = c_utf8_to_locale(lockfile);
|
||||
if (wlockfile == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = _topen(wlockfile, O_RDONLY);
|
||||
c_free_locale_string(wlockfile);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
|
||||
if (rc <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
tmp = strtol(buf, NULL, 10);
|
||||
if (tmp == 0 || tmp > 0xFFFF || errno == ERANGE) {
|
||||
/* Broken lock file */
|
||||
strerror_r(ERANGE, errbuf, sizeof(errbuf));
|
||||
if (unlink(lockfile) < 0) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
|
||||
"Unable to remove broken lock %s - %s",
|
||||
lockfile,
|
||||
errbuf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
pid = (pid_t)(tmp & 0xFFFF);
|
||||
|
||||
/* Check if process is still alive */
|
||||
if (kill(pid, 0) < 0 && errno == ESRCH) {
|
||||
/* Process is dead. Remove stale lock. */
|
||||
wlockfile = c_utf8_to_locale(lockfile);
|
||||
|
||||
if (_tunlink(wlockfile) < 0) {
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
|
||||
"Unable to remove stale lock %s - %s",
|
||||
lockfile,
|
||||
errbuf);
|
||||
}
|
||||
c_free_locale_string(wlockfile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
#endif
|
||||
|
||||
int csync_lock(const char *lockfile) {
|
||||
#ifdef _DO_CREATE_A_LOCK_FILE /* disable lock file for ownCloud client, not only _WIN32 */
|
||||
/* Check if lock already exists. */
|
||||
if (_csync_lock_read(lockfile) > 0) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Aborting, another synchronization process is running.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Creating lock file: %s", lockfile);
|
||||
|
||||
return _csync_lock_create(lockfile);
|
||||
#else
|
||||
(void) lockfile;
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void csync_lock_remove(const char *lockfile) {
|
||||
#ifdef _DO_CREATE_A_LOCK_FILE
|
||||
#ifndef _WIN32
|
||||
char errbuf[256] = {0};
|
||||
mbchar_t *wlockfile;
|
||||
|
||||
/* You can't remove the lock if it is from another process */
|
||||
if (_csync_lock_read(lockfile) == getpid()) {
|
||||
wlockfile = c_utf8_to_locale(lockfile);
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Removing lock file: %s", lockfile);
|
||||
if (_tunlink(wlockfile) < 0) {
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
|
||||
"Unable to remove lock %s - %s",
|
||||
lockfile,
|
||||
errbuf);
|
||||
}
|
||||
c_free_locale_string(wlockfile);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
(void) lockfile;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _CSYNC_LOCK_H
|
||||
#define _CSYNC_LOCK_H
|
||||
|
||||
#include "csync.h"
|
||||
|
||||
/**
|
||||
* @file csync_lock.h
|
||||
*
|
||||
* @brief File locking
|
||||
*
|
||||
* This prevents csync to start the same synchronization task twice which could
|
||||
* lead to several problems.
|
||||
*
|
||||
* @defgroup csyncLockingInternals csync file lockling internals
|
||||
* @ingroup csyncInternalAPI
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Lock the client if possible.
|
||||
*
|
||||
* This functiion tries to lock the client with a lock file.
|
||||
*
|
||||
* @param lockfile The lock file to create.
|
||||
*
|
||||
* @return 0 if the lock was successfull, less than 0 if the lock file
|
||||
* couldn't be created or if it is already locked.
|
||||
*/
|
||||
int csync_lock(const char *lockfile);
|
||||
|
||||
/**
|
||||
* @brief Remove the lockfile
|
||||
*
|
||||
* Only our own lock can be removed. This function can't remove a lock from
|
||||
* another client.
|
||||
*
|
||||
* @param lockfile The lock file to remove.
|
||||
*/
|
||||
void csync_lock_remove(const char *lockfile);
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
#endif /* _CSYNC_LOCK_H */
|
||||
/* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */
|
|
@ -35,10 +35,6 @@ add_cmocka_test(check_std_c_time std_tests/check_std_c_time.c ${TEST_TARGET_LIBR
|
|||
#add_cmocka_test(check_logger log_tests/check_log.c ${TEST_TARGET_LIBRARIES})
|
||||
|
||||
add_cmocka_test(check_csync_create csync_tests/check_csync_create.c ${TEST_TARGET_LIBRARIES})
|
||||
# Disable lock testing for us.
|
||||
# if(NOT WIN32)
|
||||
# add_cmocka_test(check_csync_lock csync_tests/check_csync_lock.c ${TEST_TARGET_LIBRARIES})
|
||||
# endif()
|
||||
add_cmocka_test(check_csync_log csync_tests/check_csync_log.c ${TEST_TARGET_LIBRARIES})
|
||||
add_cmocka_test(check_csync_exclude csync_tests/check_csync_exclude.c ${TEST_TARGET_LIBRARIES})
|
||||
add_cmocka_test(check_csync_statedb_load csync_tests/check_csync_statedb_load.c ${TEST_TARGET_LIBRARIES})
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "torture.h"
|
||||
|
||||
#define CSYNC_TEST 1
|
||||
#include "std/c_file.h"
|
||||
#include "csync_lock.h"
|
||||
|
||||
#define TEST_LOCK "/tmp/check_csync/test"
|
||||
|
||||
static void setup(void **state) {
|
||||
int rc;
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
rc = system("mkdir -p /tmp/check_csync");
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
int rc;
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
rc = system("rm -rf /tmp/check_csync");
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
static void check_csync_lock(void **state)
|
||||
{
|
||||
int rc;
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
rc = csync_lock(TEST_LOCK);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_true(c_isfile(TEST_LOCK));
|
||||
|
||||
rc = csync_lock(TEST_LOCK);
|
||||
assert_int_equal(rc, -1);
|
||||
|
||||
csync_lock_remove(TEST_LOCK);
|
||||
assert_false(c_isfile(TEST_LOCK));
|
||||
}
|
||||
|
||||
static void check_csync_lock_content(void **state)
|
||||
{
|
||||
char buf[8] = {0};
|
||||
int fd, pid, rc;
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
rc = csync_lock(TEST_LOCK);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_true(c_isfile(TEST_LOCK));
|
||||
|
||||
/* open lock file */
|
||||
fd = open(TEST_LOCK, O_RDONLY);
|
||||
assert_true(fd > 0);
|
||||
|
||||
/* read content */
|
||||
pid = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
|
||||
assert_true(pid > 0);
|
||||
|
||||
/* get pid */
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
pid = strtol(buf, NULL, 10);
|
||||
|
||||
assert_int_equal(pid, getpid());
|
||||
|
||||
csync_lock_remove(TEST_LOCK);
|
||||
assert_false(c_isfile(TEST_LOCK));
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_lock, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_lock_content, setup, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
}
|
||||
|
Loading…
Reference in a new issue