Hello community,

here is the log from the commit of package targetcli-fb for openSUSE:Factory 
checked in at 2020-02-19 12:41:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/targetcli-fb (Old)
 and      /work/SRC/openSUSE:Factory/.targetcli-fb.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "targetcli-fb"

Wed Feb 19 12:41:23 2020 rev:17 rq:775445 version:2.1.51

Changes:
--------
--- /work/SRC/openSUSE:Factory/targetcli-fb/targetcli-fb.changes        
2020-02-04 19:58:05.393487358 +0100
+++ /work/SRC/openSUSE:Factory/.targetcli-fb.new.26092/targetcli-fb.changes     
2020-02-19 12:41:23.783723502 +0100
@@ -1,0 +2,48 @@
+Wed Feb 12 17:58:15 UTC 2020 - ldun...@suse.com
+
+- Update to version 2.1.51 from 2.1.49, which includes
+  the addition of a targetcli daemon, which can be used
+  to speed up batch processing. With patches:
+  * version 2.1.51
+  * targetcli: depreciate the redundant '--tcp' option
+  * man: add daemon intro at targetcli(8) man page
+  * systemd-units: fix documentation
+  * targetclid: add man page entry
+  * daemon: load the prefs on every new connection
+  * cli: show useful hint in header area of shell in daemonized mode
+  * cli: provide a way to disable using daemon
+  * cli: show better error msg when daemon is not running
+  * Do not print err msg when signal closes socket.
+  * Handle systemd socket activation, when present.
+  * Close socket when receiving a signal to interrupt connection.
+  * Exit with success when getting a signal.
+  * Only return response to targetcli when bytes present
+  * Removed useless semicolons, as they're ignored
+  * Handle OSError correctly: use strerror to get string
+  * Tweak systemd socket-activation settings for daemon
+  * Handle Python 3.7 stricter binary vs. string rules.
+  * Fix indention for targetclid when processing output.
+  * version 2.1.50
+  * iscsi discovery_auth enable is a number not a string
+  * restoreconfig: add ability to restore/reload single target or 
storage_object
+  * Fix a syntax error in some except clauses
+  * Remove Epydoc markup from command messages
+  * targetcli: serialize multiple requests
+  * targetcli: way to enable targetclid as default choice
+  * targetclid: enable socket based activation
+  * targetclid: add daemonize component for targetcli
+  * Do not remove the first digit when auto-completing the TPG tag
+  * Remove the extra semicolon in _save_backups
+  * Add emulate_pr backstore attribute
+  * targetcli-fb: Fix raise exception error in _save_backups
+  * saveconfig: compress the backup config files
+  This replaces targetcli-fb-2.1.49.tar.xz with
+  targetcli-fb-2.1.51.tar.xz, and removes the following patches,
+  which area already upstream:
+  * Add-emulate_pr-backstore-attribute.patch
+  * do-not-remove-the-first-digit-when-auto-completing-the-tpg-tag
+  * iscsi-discovery_auth-enable-is-a-number-not-a-string
+  * saveconfig-compress-the-backup-config-files
+  * targetcli-fb-fix-raise-exception-error-in-save_backups
+
+-------------------------------------------------------------------

Old:
----
  Add-emulate_pr-backstore-attribute.patch
  do-not-remove-the-first-digit-when-auto-completing-the-tpg-tag
  iscsi-discovery_auth-enable-is-a-number-not-a-string
  saveconfig-compress-the-backup-config-files
  targetcli-fb-2.1.49.tar.xz
  targetcli-fb-fix-raise-exception-error-in-save_backups

New:
----
  targetcli-fb-2.1.51.tar.xz
  targetclid.service
  targetclid.socket

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ targetcli-fb.spec ++++++
--- /var/tmp/diff_new_pack.fBnxH7/_old  2020-02-19 12:41:24.959725766 +0100
+++ /var/tmp/diff_new_pack.fBnxH7/_new  2020-02-19 12:41:24.963725774 +0100
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           targetcli-fb
-Version:        2.1.49
+Version:        2.1.51
 Release:        0
 Summary:        A command shell for managing the Linux LIO kernel target
 License:        Apache-2.0
@@ -26,6 +26,8 @@
 URL:            https://github.com/open-iscsi/%{name}
 Source:         %{name}-%{version}.tar.xz
 Source1:        %{name}.service
+Source2:        targetclid.socket
+Source3:        targetclid.service
 BuildRequires:  %{python_module configshell-fb}
 BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module pyparsing}
@@ -45,8 +47,8 @@
 Provides:       targetcli    = %{version}-%{release}
 Provides:       targetcli-fb = %{version}-%{release}
 %endif
-Obsoletes:      targetcli
-Obsoletes:      targetcli-fb
+Obsoletes:      targetcli < %{version}-%{release}
+Obsoletes:      targetcli-fb < %{version}-%{release}
 BuildArch:      noarch
 %if 0%{?sle_version} >= 150000
 # explicit Provides advertising RBD support
@@ -56,11 +58,6 @@
 %{?systemd_ordering}
 Patch1:         Split-out-blockdev-readonly-state-detection-helper.patch
 Patch2:         rbd-support.patch
-Patch3:         saveconfig-compress-the-backup-config-files
-Patch4:         targetcli-fb-fix-raise-exception-error-in-save_backups
-Patch5:         Add-emulate_pr-backstore-attribute.patch
-Patch6:         do-not-remove-the-first-digit-when-auto-completing-the-tpg-tag
-Patch7:         iscsi-discovery_auth-enable-is-a-number-not-a-string
 
 %python_subpackages
 
@@ -90,11 +87,6 @@
 # RBD support is dependent on LIO changes present in the SLE/Leap kernel
 %patch2 -p1
 %endif
-%patch3 -p1
-%patch4 -p1
-%patch5 -p1
-%patch6 -p1
-%patch7 -p1
 
 %build
 %python_build
@@ -102,48 +94,62 @@
 %install
 %python_install
 %python_clone -a %{buildroot}%{_bindir}/targetcli
+%python_clone -a %{buildroot}%{_bindir}/targetclid
 install -d -m755 %{buildroot}%{_sysconfdir}/target
 install -d -m755 %{buildroot}%{_sysconfdir}/target/backup
 install -d -m755 %{buildroot}%{_sbindir}
 install -D -m644 targetcli.8 %{buildroot}%{_mandir}/man8/targetcli.8
-install -D -m644 %{SOURCE1} %{buildroot}%{_unitdir}/targetcli.service
+install -D -m644 targetclid.8 %{buildroot}%{_mandir}/man8/targetclid.8
+install -D -m644 %{S:1} %{buildroot}%{_unitdir}/targetcli.service
+install -D -m644 %{S:2} %{buildroot}%{_unitdir}/targetclid.socket
+install -D -m644 %{S:3} %{buildroot}%{_unitdir}/targetclid.service
 %fdupes %{buildroot}
 ln -s %{_sbindir}/service %{buildroot}/%{_sbindir}/rctargetcli
+ln -s %{_sbindir}/service %{buildroot}/%{_sbindir}/rctargetclid
 
 %post
 %python_install_alternative targetcli
+%python_install_alternative targetclid
 
 %postun
 %python_uninstall_alternative targetcli
+%python_uninstall_alternative targetclid
 
 %pre
-%{service_add_pre targetcli.service}
+%{service_add_pre targetcli.service targetclid.socket targetclid.service}
 
 %preun
-%{stop_on_removal targetcli}
+%{stop_on_removal targetcld targetcli}
+%{service_del_preun targetcli.service targetclid.socket targetclid.service}
 
 %post -n %{name}-common
-%{service_add_post targetcli.service}
+%{service_add_post targetcli.service targetclid.socket targetclid.service}
 
 %postun -n %{name}-common
-%{service_del_postun targetcli.service}
+%{service_del_postun targetcli.service targetclid.socket targetclid.service}
 
 %pre -n %{name}-common
-%{service_add_pre targetcli.service}
+%{service_add_pre targetcli.service targetclid.socket targetclid.service}
 
 %preun -n %{name}-common
-%{service_del_preun targetcli.service}
+%{service_del_preun targetcli.service targetclid.socket targetclid.service}
 
 %files %{python_files}
 %python_alternative %{_bindir}/targetcli
+%python_alternative %{_bindir}/targetclid
 %{python_sitelib}/*
 
 %files -n %{name}-common
-%doc COPYING README.md THANKS
+%license COPYING
+%doc README.md THANKS
 %dir %{_sysconfdir}/target
 %dir %{_sysconfdir}/target/backup
 %doc %{_mandir}/man8/targetcli.8%{ext_man}
+%doc %{_mandir}/man8/targetclid.8%{ext_man}
 %{_unitdir}/targetcli.service
+%{_unitdir}/targetclid.service
+%{_unitdir}/targetclid.socket
 %{_sbindir}/rctargetcli
+%{_sbindir}/rctargetclid
 
 %changelog

++++++ Split-out-blockdev-readonly-state-detection-helper.patch ++++++
--- /var/tmp/diff_new_pack.fBnxH7/_old  2020-02-19 12:41:24.983725812 +0100
+++ /var/tmp/diff_new_pack.fBnxH7/_new  2020-02-19 12:41:24.983725812 +0100
@@ -1,20 +1,22 @@
 From 7374ba0e53d8e6af4abbb02bd60f35ed541b94f5 Mon Sep 17 00:00:00 2001
 From: David Disseldorp <dd...@suse.de>
 Date: Tue, 10 Apr 2018 16:22:54 +0200
-Subject: [PATCH 3/4] Split out blockdev readonly state detection helper
+Patch-mainline: never (SUSE-specific)
+Subject: Split out blockdev readonly state detection helper
 
 So that it can be reused for RBD backstores.
 
+Note: not accepted upstream, but still needed
+here for our rbd stuff. (ldun...@suse.com)
+
 Signed-off-by: David Disseldorp <dd...@suse.de>
 ---
- targetcli/ui_backstore.py | 40 ++++++++++++++++++++--------------------
+ targetcli/ui_backstore.py |   40 ++++++++++++++++++++--------------------
  1 file changed, 20 insertions(+), 20 deletions(-)
 
-diff --git a/targetcli/ui_backstore.py b/targetcli/ui_backstore.py
-index 546d9d2..57dedb1 100644
 --- a/targetcli/ui_backstore.py
 +++ b/targetcli/ui_backstore.py
-@@ -117,6 +117,25 @@ def complete_path(path, stat_fn):
+@@ -119,6 +119,25 @@ def complete_path(path, stat_fn):
      return sorted(filtered,
                    key=lambda s: '~'+s if s.endswith('/') else s)
  
@@ -40,7 +42,7 @@
  
  class UIALUATargetPortGroup(UIRTSLibNode):
      '''
-@@ -519,25 +538,6 @@ class UIBlockBackstore(UIBackstore):
+@@ -536,25 +555,6 @@ class UIBlockBackstore(UIBackstore):
          self.so_cls = UIBlockStorageObject
          UIBackstore.__init__(self, 'block', parent)
  
@@ -65,8 +67,8 @@
 -
      def ui_command_create(self, name, dev, readonly=None, wwn=None):
          '''
-         Creates an Block Storage object. I{dev} is the path to the TYPE_DISK
-@@ -548,7 +548,7 @@ class UIBlockBackstore(UIBackstore):
+         Creates an Block Storage object. "dev" is the path to the TYPE_DISK
+@@ -565,7 +565,7 @@ class UIBlockBackstore(UIBackstore):
          ro_string = self.ui_eval_param(readonly, 'string', None)
          if ro_string == None:
              # attempt to detect block device readonly state via ioctl
@@ -75,6 +77,3 @@
          else:
              readonly = self.ui_eval_param(readonly, 'bool', False)
  
--- 
-2.13.6
-

++++++ _service ++++++
--- /var/tmp/diff_new_pack.fBnxH7/_old  2020-02-19 12:41:24.995725835 +0100
+++ /var/tmp/diff_new_pack.fBnxH7/_new  2020-02-19 12:41:24.999725843 +0100
@@ -5,9 +5,9 @@
     <param name="subdir"></param>
     <param name="filename">targetcli-fb</param>
     <param name="versionformat">@PARENT_TAG@</param>
-    <param name="versionrewrite-pattern">v(\d*\.\d*\.)fb(\d*)</param>
+    <param name="versionrewrite-pattern">v(\d*\.\d*\.)(\d*)</param>
     <param name="versionrewrite-replacement">\1\2</param>
-    <param name="revision">v2.1.fb49</param>
+    <param name="revision">v2.1.51</param>
     <param name="changesgenerate">enable</param>
   </service>
   <service name="recompress" mode="disabled">

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.fBnxH7/_old  2020-02-19 12:41:25.011725866 +0100
+++ /var/tmp/diff_new_pack.fBnxH7/_new  2020-02-19 12:41:25.011725866 +0100
@@ -1,4 +1,4 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/open-iscsi/targetcli-fb.git</param>
-              <param 
name="changesrevision">4d08771c0e6bf3cacba2ed3d3127dd10a86a7847</param></service></servicedata>
\ No newline at end of file
+              <param 
name="changesrevision">06076aba7e9e9bd4a1e84bac61e85265e8075b8e</param></service></servicedata>
\ No newline at end of file

++++++ targetcli-fb-2.1.49.tar.xz -> targetcli-fb-2.1.51.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/daemon/targetclid 
new/targetcli-fb-2.1.51/daemon/targetclid
--- old/targetcli-fb-2.1.49/daemon/targetclid   1970-01-01 01:00:00.000000000 
+0100
+++ new/targetcli-fb-2.1.51/daemon/targetclid   2019-11-06 13:31:26.000000000 
+0100
@@ -0,0 +1,276 @@
+#!/usr/bin/python
+
+'''
+targetclid
+
+This file is part of targetcli-fb.
+Copyright (c) 2019 by Red Hat, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may
+not use this file except in compliance with the License. You may obtain
+a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations
+under the License.
+'''
+
+from __future__ import print_function
+from targetcli import UIRoot
+from targetcli import __version__ as targetcli_version
+from configshell_fb import ConfigShell
+from os import getuid, getenv, unlink
+from threading import Thread
+
+import sys
+import socket
+import struct
+import fcntl
+import signal
+import errno
+
+
+err = sys.stderr
+
+class TargetCLI:
+    def __init__(self):
+        '''
+        initializer
+        '''
+        # socket for unix communication
+        self.socket_path = '/var/run/targetclid.sock'
+        # pid file for defending on multiple daemon runs
+        self.pid_file = '/var/run/targetclid.pid'
+
+        self.NoSignal = True
+        self.sock = None
+
+        # shell console methods
+        self.shell = ConfigShell(getenv("TARGETCLI_HOME", '~/.targetcli'))
+        self.con = self.shell.con
+        self.display = self.shell.con.display
+        self.render = self.shell.con.render_text
+
+        # Handle SIGINT SIGTERM SIGHUP gracefully
+        signal.signal(signal.SIGINT, self.signal_handler)
+        signal.signal(signal.SIGTERM, self.signal_handler)
+        signal.signal(signal.SIGHUP, self.signal_handler)
+
+        try:
+            self.pfd = open(self.pid_file, 'w+')
+        except IOError as e:
+            self.display(
+                self.render(
+                    "opening pidfile failed: %s" %str(e),
+                    'red'))
+            sys.exit(1)
+
+        self.try_pidfile_lock()
+
+        is_root = False
+        if getuid() == 0:
+            is_root = True
+
+        try:
+            root_node = UIRoot(self.shell, as_root=is_root)
+            root_node.refresh()
+        except Exception as error:
+            self.display(self.render(str(error), 'red'))
+            if not is_root:
+                self.display(self.render("Retry as root.", 'red'))
+            self.pfd.close()
+            sys.exit(1)
+
+        # Keep track, for later use
+        self.con_stdout_ = self.con._stdout
+        self.con_stderr_ = self.con._stderr
+
+
+    def __del__(self):
+        '''
+        destructor
+        '''
+        if hasattr(self, 'pfd'):
+            self.pfd.close()
+
+
+    def signal_handler(self, signum, frame):
+        '''
+        signal handler
+        '''
+        self.NoSignal = False
+        if self.sock:
+            self.sock.close()
+
+
+    def try_pidfile_lock(self):
+        '''
+        get lock on pidfile, which is to check if targetclid is running
+        '''
+        # check if targetclid is already running
+        lock = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
+        try:
+            fcntl.fcntl(self.pfd, fcntl.F_SETLK, lock)
+        except Exception:
+            self.display(self.render("targetclid is already running...", 
'red'))
+            self.pfd.close()
+            sys.exit(1)
+
+
+    def release_pidfile_lock(self):
+        '''
+        release lock on pidfile
+        '''
+        lock = struct.pack('hhllhh', fcntl.F_UNLCK, 0, 0, 0, 0, 0)
+        try:
+            fcntl.fcntl(self.pfd, fcntl.F_SETLK, lock)
+        except Exception as e:
+            self.display(
+                self.render(
+                    "fcntl(UNLCK) on pidfile failed: %s" %str(e),
+                    'red'))
+            self.pfd.close()
+            sys.exit(1)
+        self.pfd.close()
+
+
+    def client_thread(self, connection):
+        '''
+        Handle commands from client
+        '''
+        # load the prefs
+        self.shell.prefs.load()
+
+        still_listen = True
+        # Receive the data in small chunks and retransmit it
+        while still_listen:
+            data = connection.recv(65535)
+            if b'-END@OF@DATA-' in data:
+                connection.close()
+                still_listen = False
+            else:
+                self.con._stdout = self.con._stderr = f = 
open("/tmp/data.txt", "w")
+                try:
+                    # extract multiple commands delimited with '%'
+                    list_data = data.decode().split('%')
+                    for cmd in list_data:
+                        self.shell.run_cmdline(cmd)
+                except Exception as e:
+                    print(str(e), file=f) # push error to stream
+
+                # Restore
+                self.con._stdout = self.con_stdout_
+                self.con._stderr = self.con_stderr_
+                f.close()
+
+                with open('/tmp/data.txt', 'r') as f:
+                    output = f.read()
+                    var = struct.pack('i', len(output))
+                    connection.sendall(var) # length of string
+                    if len(output):
+                        connection.sendall(output.encode()) # actual string
+
+
+def usage():
+    print("Usage: %s [--version|--help]" % sys.argv[0], file=err)
+    print("  --version\t\tPrint version", file=err)
+    print("  --help\t\tPrint this information", file=err)
+    sys.exit(0)
+
+
+def version():
+    print("%s version %s" % (sys.argv[0], targetcli_version), file=err)
+    sys.exit(0)
+
+
+def usage_version(cmd):
+    if cmd in ("help", "--help", "-h"):
+        usage()
+
+    if cmd in ("version", "--version", "-v"):
+        version()
+
+
+def main():
+    '''
+    start targetclid
+    '''
+    if len(sys.argv) > 1:
+        usage_version(sys.argv[1])
+        print("unrecognized option: %s" % (sys.argv[1]))
+        sys.exit(-1)
+
+    to = TargetCLI()
+
+    if getenv('LISTEN_PID'):
+        # the systemd-activation path, using next available FD
+        fn = sys.stderr.fileno() + 1
+        try:
+            sock = socket.fromfd(fn, socket.AF_UNIX, socket.SOCK_STREAM)
+        except socket.error as err:
+            to.display(to.render(err.strerror, 'red'))
+            sys.exit(1)
+
+        # save socket so a signal can clea it up
+        to.sock = sock
+    else:
+        # Make sure file doesn't exist already
+        try:
+            unlink(to.socket_path)
+        except:
+            pass
+
+        # Create a TCP/IP socket
+        try:
+            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        except socket.error as err:
+            to.display(to.render(err.strerror, 'red'))
+            sys.exit(1)
+
+        # save socket so a signal can clea it up
+        to.sock = sock
+
+        # Bind the socket path
+        try:
+            sock.bind(to.socket_path)
+        except socket.error as err:
+            to.display(to.render(err.strerror, 'red'))
+            sys.exit(1)
+
+        # Listen for incoming connections
+        try:
+            sock.listen(1)
+        except socket.error as err:
+            to.display(to.render(err.strerror, 'red'))
+            sys.exit(1)
+
+    while to.NoSignal:
+        try:
+            # Wait for a connection
+            connection, client_address = sock.accept()
+        except socket.error as err:
+            if err.errno != errno.EBADF or to.NoSignal:
+                to.display(to.render(err.strerror, 'red'))
+            break
+
+        thread = Thread(target=to.client_thread, args=(connection,))
+        thread.start()
+        try:
+            thread.join()
+        except:
+            to.display(to.render(str(error), 'red'))
+
+    to.release_pidfile_lock()
+
+    if not to.NoSignal:
+        to.display(to.render("Signal received, quiting gracefully!", 'green'))
+        sys.exit(0)
+    sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/scripts/targetcli 
new/targetcli-fb-2.1.51/scripts/targetcli
--- old/targetcli-fb-2.1.49/scripts/targetcli   2018-09-05 14:08:11.000000000 
+0200
+++ new/targetcli-fb-2.1.51/scripts/targetcli   2019-11-06 13:31:26.000000000 
+0100
@@ -24,10 +24,22 @@
 from targetcli import UIRoot
 from rtslib_fb import RTSLibError
 from configshell_fb import ConfigShell, ExecutionError
-import sys
 from targetcli import __version__ as targetcli_version
 
+import sys
+import socket
+import struct
+import readline
+import six
+import fcntl
+
 err = sys.stderr
+# lockfile for serializing multiple targetcli requests
+lock_file = '/var/run/targetcli.lock'
+socket_path = '/var/run/targetclid.sock'
+hints = ['/', 'backstores/', 'iscsi/', 'loopback/', 'vhost/', 'xen-pvscsi/',
+         'cd', 'pwd', 'ls', 'set', 'get', 'help', 'refresh', 'status',
+         'clearconfig', 'restoreconfig', 'saveconfig', 'exit']
 
 class TargetCLI(ConfigShell):
     default_prefs = {'color_path': 'magenta',
@@ -51,14 +63,16 @@
                      'auto_save_on_exit': True,
                      'max_backup_files': '10',
                      'auto_add_default_portal': True,
+                     'auto_use_daemon': False,
                     }
 
 def usage():
-    print("Usage: %s [--version|--help|CMD]" % sys.argv[0], file=err)
+    print("Usage: %s [--version|--help|CMD|--disable-daemon]" % sys.argv[0], 
file=err)
     print("  --version\t\tPrint version", file=err)
     print("  --help\t\tPrint this information", file=err)
     print("  CMD\t\t\tRun targetcli shell command and exit", file=err)
     print("  <nothing>\t\tEnter configuration shell", file=err)
+    print("  --disable-daemon\tTurn-off the global auto use daemon flag", 
file=err)
     print("See man page for more information.", file=err)
     sys.exit(-1)
 
@@ -66,16 +80,153 @@
     print("%s version %s" % (sys.argv[0], targetcli_version), file=err)
     sys.exit(0)
 
+def usage_version(cmd):
+    if cmd in ("help", "--help", "-h"):
+        usage()
+
+    if cmd in ("version", "--version", "-v"):
+        version()
+
+def try_op_lock(shell, lkfd):
+    '''
+    acquire a blocking lock on lockfile, to serialize multiple requests
+    '''
+    try:
+        fcntl.flock(lkfd, fcntl.LOCK_EX)  # wait here until ongoing request is 
finished
+    except Exception as e:
+        shell.con.display(
+            shell.con.render_text(
+                "taking lock on lockfile failed: %s" %str(e),
+                'red'))
+        sys.exit(1)
+
+def release_op_lock(shell, lkfd):
+    '''
+    release blocking lock on lockfile, which can allow other requests process
+    '''
+    try:
+        fcntl.flock(lkfd, fcntl.LOCK_UN)  # allow other requests now
+    except Exception as e:
+        shell.con.display(
+            shell.con.render_text(
+                "unlock on lockfile failed: %s" %str(e),
+                'red'))
+        sys.exit(1)
+    lkfd.close()
+
+def completer(text, state):
+    options = [x for x in hints if x.startswith(text)]
+    try:
+        return options[state]
+    except IndexError:
+        return None
+
+def call_daemon(shell, req):
+    try:
+        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    except socket.error as err:
+        shell.con.display(shell.con.render_text(err, 'red'))
+        sys.exit(1)
+
+    try:
+        sock.connect(socket_path)
+    except socket.error as err:
+        shell.con.display(shell.con.render_text(err, 'red'))
+        shell.con.display(
+            shell.con.render_text("Currently auto_use_daemon is true, "
+                "hence please make sure targetclid daemon is running ...\n"
+                "(or)\nIncase if you wish to turn auto_use_daemon to false "
+                "then run '#targetcli --disable-daemon'", 'red'))
+        sys.exit(1)
+
+    try:
+        # send request
+        sock.sendall(req)
+    except socket.error as err:
+        shell.con.display(shell.con.render_text(err, 'red'))
+        sys.exit(1)
+
+    var = sock.recv(4) # get length of data
+    sending = struct.unpack('i', var)
+    amount_expected = sending[0]
+    amount_received = 0
+
+    # get the actual data in chunks
+    while amount_received < amount_expected:
+        data = sock.recv(1024)
+        amount_received += len(data)
+        print(data.decode(), end ="")
+
+    sock.send(b'-END@OF@DATA-')
+    sock.close()
+    sys.exit(0)
+
+def get_arguments(shell):
+    readline.set_completer(completer)
+    readline.set_completer_delims('')
+
+    if 'libedit' in readline.__doc__:
+        readline.parse_and_bind("bind ^I rl_complete")
+    else:
+        readline.parse_and_bind("tab: complete")
+
+    if len(sys.argv) > 1:
+        command = " ".join(sys.argv[1:])
+    else:
+        inputs = []
+        shell.con.display("targetcli shell version %s\n"
+                        "Entering targetcli batch mode for daemonized 
approach.\n"
+                        "Enter multiple commands separated by newline and "
+                        "type 'exit' to run them all in one go.\n"
+                        % targetcli_version)
+        while True:
+            shell.con.raw_write("/> ")
+            command = six.moves.input()
+            if command.lower() == "exit":
+                break
+            inputs.append(command)
+        command = '%'.join(inputs)  # delimit multiple commands with '%'
+
+    if not command:
+        sys.exit(1)
+
+    usage_version(command);
+
+    return command
+
 def main():
     '''
     Start the targetcli shell.
     '''
+    shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli'))
+
+    is_root = False
     if getuid() == 0:
         is_root = True
-    else:
-        is_root = False
 
-    shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli'))
+    try:
+        lkfd = open(lock_file, 'w+');
+    except IOError as e:
+        shell.con.display(
+                shell.con.render_text("opening lockfile failed: %s" %str(e),
+                    'red'))
+        sys.exit(1)
+
+    try_op_lock(shell, lkfd)
+
+    use_daemon = False
+    if shell.prefs['auto_use_daemon']:
+        use_daemon = True
+
+    disable_daemon=False
+    if len(sys.argv) > 1:
+        usage_version(sys.argv[1])
+        if sys.argv[1] in ("disable-daemon", "--disable-daemon"):
+            disable_daemon=True
+
+    if use_daemon and not disable_daemon:
+        call_daemon(shell, get_arguments(shell).encode())
+        # does not return
 
     try:
         root_node = UIRoot(shell, as_root=is_root)
@@ -87,14 +238,11 @@
         sys.exit(-1)
 
     if len(sys.argv) > 1:
-        if sys.argv[1] in ("--help", "-h"):
-            usage()
-
-        if sys.argv[1] in ("--version", "-v"):
-            version()
-
         try:
-            shell.run_cmdline(" ".join(sys.argv[1:]))
+            if disable_daemon:
+                shell.run_cmdline('set global auto_use_daemon=false')
+            else:
+                shell.run_cmdline(" ".join(sys.argv[1:]))
         except Exception as e:
             print(str(e), file=sys.stderr)
             sys.exit(1)
@@ -117,6 +265,8 @@
         shell.log.info("Global pref auto_save_on_exit=true")
         root_node.ui_command_saveconfig()
 
+    release_op_lock(shell, lkfd)
+
 
 if __name__ == "__main__":
     main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/setup.py 
new/targetcli-fb-2.1.51/setup.py
--- old/targetcli-fb-2.1.49/setup.py    2018-09-05 14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/setup.py    2019-11-06 13:31:26.000000000 +0100
@@ -30,7 +30,10 @@
     maintainer_email = 'agro...@redhat.com',
     url = 'http://github.com/open-iscsi/targetcli-fb',
     packages = ['targetcli'],
-    scripts = ['scripts/targetcli'],
+    scripts = [
+               'scripts/targetcli',
+               'daemon/targetclid'
+              ],
     classifiers = [
         "Programming Language :: Python",
         "Programming Language :: Python :: 3",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/systemd/targetclid.service 
new/targetcli-fb-2.1.51/systemd/targetclid.service
--- old/targetcli-fb-2.1.49/systemd/targetclid.service  1970-01-01 
01:00:00.000000000 +0100
+++ new/targetcli-fb-2.1.51/systemd/targetclid.service  2019-11-06 
13:31:26.000000000 +0100
@@ -0,0 +1,13 @@
+[Unit]
+Description=Targetcli daemon
+Documentation=man:targetclid(8)
+After=network.target
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/targetclid
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+Also=targetclid.socket
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/systemd/targetclid.socket 
new/targetcli-fb-2.1.51/systemd/targetclid.socket
--- old/targetcli-fb-2.1.49/systemd/targetclid.socket   1970-01-01 
01:00:00.000000000 +0100
+++ new/targetcli-fb-2.1.51/systemd/targetclid.socket   2019-11-06 
13:31:26.000000000 +0100
@@ -0,0 +1,9 @@
+[Unit]
+Description=targetclid socket
+Documentation=man:targetclid(8)
+
+[Socket]
+ListenStream=/var/run/targetclid.sock
+
+[Install]
+WantedBy=sockets.target
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetcli/ui_backstore.py 
new/targetcli-fb-2.1.51/targetcli/ui_backstore.py
--- old/targetcli-fb-2.1.49/targetcli/ui_backstore.py   2018-09-05 
14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/targetcli/ui_backstore.py   2019-11-06 
13:31:26.000000000 +0100
@@ -59,14 +59,14 @@
     '''
     This function converts human-readable amounts of bytes to bytes.
     It understands the following units :
-        - I{B} or no unit present for Bytes
-        - I{k}, I{K}, I{kB}, I{KB} for kB (kilobytes)
-        - I{m}, I{M}, I{mB}, I{MB} for MB (megabytes)
-        - I{g}, I{G}, I{gB}, I{GB} for GB (gigabytes)
-        - I{t}, I{T}, I{tB}, I{TB} for TB (terabytes)
+        - B or no unit present for Bytes
+        - k, K, kB, KB for kB (kilobytes)
+        - m, M, mB, MB for MB (megabytes)
+        - g, G, gB, GB for GB (gigabytes)
+        - t, T, tB, TB for TB (terabytes)
 
-    Note: The definition of I{kilo} defaults to 1kB = 1024Bytes.
-    Strictly speaking, those should not be called I{kB} but I{kiB}.
+    Note: The definition of kilo defaults to 1kB = 1024Bytes.
+    Strictly speaking, those should not be called "kB" but "kiB".
     You can override that with the optional kilo parameter.
 
     @param hsize: The human-readable version of the Bytes amount to convert
@@ -286,13 +286,13 @@
 
     def ui_command_delete(self, name, save=None):
         '''
-        Recursively deletes the storage object having the specified I{name}. If
+        Recursively deletes the storage object having the specified name. If
         there are LUNs using this storage object, they will be deleted too.
 
         EXAMPLE
         =======
-        B{delete mystorage}
-        -------------------
+        delete mystorage
+        ----------------
         Deletes the storage object named mystorage, and all associated LUNs.
         '''
         self.assert_root()
@@ -354,7 +354,7 @@
     def ui_command_create(self, name, dev):
         '''
         Creates a PSCSI storage object, with supplied name and SCSI device. The
-        SCSI device I{dev} can either be a path name to the device, in which
+        SCSI device "dev" can either be a path name to the device, in which
         case it is recommended to use the /dev/disk/by-id hierarchy to have
         consistent naming should your physical SCSI system be modified, or an
         SCSI device ID in the H:C:T:L format, which is not recommended as SCSI
@@ -383,17 +383,17 @@
 
     def ui_command_create(self, name, size, nullio=None, wwn=None):
         '''
-        Creates an RDMCP storage object. I{size} is the size of the ramdisk.
+        Creates an RDMCP storage object. "size" is the size of the ramdisk.
 
         SIZE SYNTAX
         ===========
         - If size is an int, it represents a number of bytes.
         - If size is a string, the following units can be used:
-            - B{B} or no unit present for bytes
-            - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes)
-            - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes)
-            - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes)
-            - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes)
+            - B or no unit present for bytes
+            - k, K, kB, KB for kB (kilobytes)
+            - m, M, mB, MB for MB (megabytes)
+            - g, G, gB, GB for GB (gigabytes)
+            - t, T, tB, TB for TB (terabytes)
         '''
         self.assert_root()
 
@@ -445,14 +445,14 @@
     def ui_command_create(self, name, file_or_dev, size=None, write_back=None,
                           sparse=None, wwn=None):
         '''
-        Creates a FileIO storage object. If I{file_or_dev} is a path
-        to a regular file to be used as backend, then the I{size}
-        parameter is mandatory. Else, if I{file_or_dev} is a path to a
-        block device, the size parameter B{must} be ommited. If
-        present, I{size} is the size of the file to be used, I{file}
-        the path to the file or I{dev} the path to a block device. The
-        I{write_back} parameter is a boolean controlling write
-        caching. It is enabled by default. The I{sparse} parameter is
+        Creates a FileIO storage object. If "file_or_dev" is a path
+        to a regular file to be used as backend, then the "size"
+        parameter is mandatory. Else, if "file_or_dev" is a path to a
+        block device, the size parameter must be omitted. If
+        present, "size" is the size of the file to be used, "file"
+        the path to the file or "dev" the path to a block device. The
+        "write_back" parameter is a boolean controlling write
+        caching. It is enabled by default. The "sparse" parameter is
         only applicable when creating a new backing file. It is a
         boolean stating if the created file should be created as a
         sparse file (the default), or fully initialized.
@@ -461,11 +461,11 @@
         ===========
         - If size is an int, it represents a number of bytes.
         - If size is a string, the following units can be used:
-            - B{B} or no unit present for bytes
-            - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes)
-            - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes)
-            - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes)
-            - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes)
+            - B or no unit present for bytes
+            - k, K, kB, KB for kB (kilobytes)
+            - m, M, mB, MB for MB (megabytes)
+            - g, G, gB, GB for GB (gigabytes)
+            - t, T, tB, TB for TB (terabytes)
         '''
         self.assert_root()
 
@@ -557,7 +557,7 @@
 
     def ui_command_create(self, name, dev, readonly=None, wwn=None):
         '''
-        Creates an Block Storage object. I{dev} is the path to the TYPE_DISK
+        Creates an Block Storage object. "dev" is the path to the TYPE_DISK
         block device to use.
         '''
         self.assert_root()
@@ -628,11 +628,11 @@
         ===========
         - If size is an int, it represents a number of bytes.
         - If size is a string, the following units can be used:
-            - B{B} or no unit present for bytes
-            - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes)
-            - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes)
-            - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes)
-            - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes)
+            - B or no unit present for bytes
+            - k, K, kB, KB for kB (kilobytes)
+            - m, M, mB, MB for MB (megabytes)
+            - g, G, gB, GB for GB (gigabytes)
+            - t, T, tB, TB for TB (terabytes)
         '''
 
         size = human_to_bytes(size)
@@ -689,6 +689,7 @@
         'emulate_tpws': ('number', 'If set to 1, enable Thin Provisioning 
Write Same.'),
         'emulate_ua_intlck_ctrl': ('number', 'If set to 1, enable Unit 
Attention Interlock.'),
         'emulate_write_cache': ('number', 'If set to 1, turn on Write Cache 
Enable.'),
+        'emulate_pr': ('number', 'If set to 1, enable SCSI Reservations.'),
         'enforce_pr_isids': ('number', 'If set to 1, enforce persistent 
reservation ISIDs.'),
         'force_pr_aptpl': ('number', 'If set to 1, force SPC-3 PR Activate 
Persistence across Target Power Loss operation.'),
         'fabric_max_sectors': ('number', 'Maximum number of sectors the fabric 
can transfer at once.'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetcli/ui_node.py 
new/targetcli-fb-2.1.51/targetcli/ui_node.py
--- old/targetcli-fb-2.1.49/targetcli/ui_node.py        2018-09-05 
14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/targetcli/ui_node.py        2019-11-06 
13:31:26.000000000 +0100
@@ -49,6 +49,9 @@
         self.define_config_group_param(
             'global', 'max_backup_files', 'string',
             'Max no. of configurations to be backed up in /etc/target/backup/ 
directory.')
+        self.define_config_group_param(
+            'global', 'auto_use_daemon', 'bool',
+            'If true, commands will be sent to targetclid.')
 
     def assert_root(self):
         '''
@@ -95,7 +98,7 @@
 
         SEE ALSO
         ========
-        B{ls}
+        ls
         '''
         description, is_healthy = self.summary()
         self.shell.log.info("Status for %s: %s" % (self.path, description))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetcli/ui_root.py 
new/targetcli-fb-2.1.51/targetcli/ui_root.py
--- old/targetcli-fb-2.1.49/targetcli/ui_root.py        2018-09-05 
14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/targetcli/ui_root.py        2019-11-06 
13:31:26.000000000 +0100
@@ -24,6 +24,7 @@
 import shutil
 import stat
 import filecmp
+import gzip
 
 from configshell_fb import ExecutionError
 from rtslib_fb import RTSRoot
@@ -62,6 +63,38 @@
             if fm.wwns == None or any(fm.wwns):
                 UIFabricModule(fm, self)
 
+    def _compare_files(self, backupfile, savefile):
+        '''
+        Compare backfile and saveconfig file
+        '''
+        if (os.path.splitext(backupfile)[1] == '.gz'):
+            try:
+                with gzip.open(backupfile, 'rb') as fbkp:
+                    fdata_bkp = fbkp.read()
+            except IOError as e:
+                self.shell.log.warning("Could not gzip open backupfile %s: %s"
+                                       % (backupfile, e.strerror))
+
+        else:
+            try:
+                with open(backupfile, 'rb') as fbkp:
+                    fdata_bkp = fbkp.read()
+            except IOError as e:
+                self.shell.log.warning("Could not open backupfile %s: %s"
+                                       % (backupfile, e.strerror))
+
+        try:
+            with open(savefile, 'rb') as f:
+                fdata = f.read()
+        except IOError as e:
+            self.shell.log.warning("Could not open saveconfig file %s: %s"
+                                   % (savefile, e.strerror))
+
+        if fdata_bkp == fdata:
+            return True
+        else:
+            return False
+
     def _save_backups(self, savefile):
         '''
         Take backup of config-file if needed.
@@ -72,29 +105,30 @@
 
         backup_dir = os.path.dirname(savefile) + "/backup/"
         backup_name = "saveconfig-" + \
-                      datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json"
+                      datetime.now().strftime("%Y%m%d-%H:%M:%S") + "-json.gz"
         backupfile = backup_dir + backup_name
         backup_error = None
 
         if not os.path.exists(backup_dir):
             try:
-                os.makedirs(backup_dir);
+                os.makedirs(backup_dir)
             except OSError as exe:
                 raise ExecutionError("Cannot create backup directory [%s] %s."
-                                     % (backup_dir, exc.strerror))
+                                     % (backup_dir, exe.strerror))
 
         # Only save backups if savefile exits
         if not os.path.exists(savefile):
             return
 
         backed_files_list = sorted(glob(os.path.dirname(savefile) + \
-                                   "/backup/*.json"))
+                                   "/backup/saveconfig-*json*"))
 
         # Save backup if backup dir is empty, or savefile is differnt from 
recent backup copy
-        if not backed_files_list or not filecmp.cmp(backed_files_list[-1], 
savefile):
+        if not backed_files_list or not 
self._compare_files(backed_files_list[-1], savefile):
             try:
-                shutil.copy(savefile, backupfile)
-
+                with open(savefile, 'rb') as f_in, gzip.open(backupfile, 'wb') 
as f_out:
+                    shutil.copyfileobj(f_in, f_out)
+                    f_out.flush()
             except IOError as ioe:
                 backup_error = ioe.strerror or "Unknown error"
 
@@ -139,7 +173,8 @@
 
         self.shell.log.info("Configuration saved to %s" % savefile)
 
-    def ui_command_restoreconfig(self, savefile=default_save_file, 
clear_existing=False):
+    def ui_command_restoreconfig(self, savefile=default_save_file, 
clear_existing=False,
+                                 target=None, storage_object=None):
         '''
         Restores configuration from a file.
         '''
@@ -151,7 +186,10 @@
             self.shell.log.info("Restore file %s not found" % savefile)
             return
 
-        errors = self.rtsroot.restore_from_file(savefile, clear_existing)
+        target = self.ui_eval_param(target, 'string', None)
+        storage_object = self.ui_eval_param(storage_object, 'string', None)
+        errors = self.rtsroot.restore_from_file(savefile, clear_existing,
+                                                target, storage_object)
 
         self.refresh()
 
@@ -202,15 +240,15 @@
         PARAMETERS
         ==========
 
-        I{action}
-        ---------
-        The I{action} is one of:
-            - B{list} gives a short session list
-            - B{detail} gives a detailed list
-
-        I{sid}
+        action
         ------
-        You can specify an I{sid} to only list this one,
+        The action is one of:
+            - `list`` gives a short session list
+            - `detail` gives a detailed list
+
+        sid
+        ---
+        You can specify an "sid" to only list this one,
         with or without details.
 
         SEE ALSO
@@ -283,4 +321,3 @@
                 indent_print("(no open sessions)", base_steps)
             else:
                 raise ExecutionError("no session found with sid %i" % int(sid))
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetcli/ui_target.py 
new/targetcli-fb-2.1.51/targetcli/ui_target.py
--- old/targetcli-fb-2.1.49/targetcli/ui_target.py      2018-09-05 
14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/targetcli/ui_target.py      2019-11-06 
13:31:26.000000000 +0100
@@ -34,7 +34,8 @@
 from .ui_node import UINode, UIRTSLibNode
 
 auth_params = ('userid', 'password', 'mutual_userid', 'mutual_password')
-discovery_params = auth_params + ("enable",)
+int_params = ('enable',)
+discovery_params = auth_params + int_params
 
 class UIFabricModule(UIRTSLibNode):
     '''
@@ -47,8 +48,12 @@
         self.refresh()
         if self.rtsnode.has_feature('discovery_auth'):
             for param in discovery_params:
-                self.define_config_group_param('discovery_auth',
-                                               param, 'string')
+                if param in int_params:
+                    self.define_config_group_param('discovery_auth',
+                                                   param, 'number')
+                else:
+                    self.define_config_group_param('discovery_auth',
+                                                   param, 'string')
         self.refresh()
 
     # Support late params
@@ -167,18 +172,18 @@
 
     def ui_command_create(self, wwn=None):
         '''
-        Creates a new target. The I{wwn} format depends on the transport(s)
-        supported by the fabric module. If the I{wwn} is ommited, then a
+        Creates a new target. The "wwn" format depends on the transport(s)
+        supported by the fabric module. If "wwn" is omitted, then a
         target will be created using either a randomly generated WWN of the
         proper type, or the first unused WWN in the list of possible WWNs if
         one is available. If WWNs are constrained to a list (i.e. for hardware
         targets addresses) and all WWNs are in use, the target creation will
-        fail. Use the B{info} command to get more information abour WWN type
+        fail. Use the `info` command to get more information abour WWN type
         and possible values.
 
         SEE ALSO
         ========
-        B{info}
+        info
         '''
         self.assert_root()
 
@@ -223,12 +228,12 @@
 
     def ui_command_delete(self, wwn):
         '''
-        Recursively deletes the target with the specified I{wwn}, and all
+        Recursively deletes the target with the specified wwn, and all
         objects hanging under it.
 
         SEE ALSO
         ========
-        B{create}
+        create
         '''
         self.assert_root()
         target = Target(self.rtsnode, wwn, mode='lookup')
@@ -262,7 +267,7 @@
     def ui_command_info(self):
         '''
         Displays information about the fabric module, notably the supported
-        transports(s) and accepted B{wwn} format(s), as long as supported
+        transports(s) and accepted wwn format(s), along with supported
         features.
         '''
         fabric = self.rtsnode
@@ -308,13 +313,13 @@
     def ui_command_create(self, tag=None):
         '''
         Creates a new Target Portal Group within the target. The
-        I{tag} must be a positive integer value, optionally prefaced
+        tag must be a positive integer value, optionally prefaced
         by 'tpg'. If omitted, the next available Target Portal Group
         Tag (TPGT) will be used.
 
         SEE ALSO
         ========
-        B{delete}
+        delete
         '''
         self.assert_root()
 
@@ -351,12 +356,12 @@
 
     def ui_command_delete(self, tag):
         '''
-        Deletes the Target Portal Group with TPGT I{tag} from the target. The
-        I{tag} must be a positive integer matching an existing TPGT.
+        Deletes the Target Portal Group with TPGT "tag" from the target. The
+        tag must be a positive integer matching an existing TPGT.
 
         SEE ALSO
         ========
-        B{create}
+        create
         '''
         self.assert_root()
         if tag.startswith("tpg"):
@@ -384,7 +389,7 @@
         @rtype: list of str
         '''
         if current_param == 'tag':
-            tags = [child.name[4:] for child in self.children]
+            tags = [child.name[3:] for child in self.children]
             completions = [tag for tag in tags if tag.startswith(text)]
         else:
             completions = []
@@ -515,7 +520,7 @@
 
         SEE ALSO
         ========
-        B{disable status}
+        disable status
         '''
         self.assert_root()
         if self.rtsnode.enable:
@@ -533,7 +538,7 @@
 
         SEE ALSO
         ========
-        B{enable status}
+        enable status
         '''
         self.assert_root()
         if self.rtsnode.enable:
@@ -582,18 +587,19 @@
 
     def ui_command_create(self, wwn, add_mapped_luns=None):
         '''
-        Creates a Node ACL for the initiator node with the specified I{wwn}.
-        The node's I{wwn} must match the expected WWN Type of the target's
+        Creates a Node ACL for the initiator node with the specified wwn.
+        The node's wwn must match the expected WWN Type of the target's
         fabric module.
 
-        If I{add_mapped_luns} is omitted, the global parameter
-        B{auto_add_mapped_luns} will be used, else B{true} or B{false} are
-        accepted. If B{true}, then after creating the ACL, mapped LUNs will be
-        automatically created for all existing LUNs.
+        "add_mapped_luns" can be "true" of "false". If true, then
+        after creating the ACL, mapped LUNs will be automatically
+        created for all existing LUNs. If the parameter is omitted,
+        the global parameter "auto_add_mapped_luns" is used.
 
         SEE ALSO
         ========
-        B{delete}
+        delete
+
         '''
         self.assert_root()
 
@@ -614,11 +620,11 @@
 
     def ui_command_delete(self, wwn):
         '''
-        Deletes the Node ACL with the specified I{wwn}.
+        Deletes the Node ACL with the specified wwn.
 
         SEE ALSO
         ========
-        B{create}
+        create
         '''
         self.assert_root()
         node_acl = NodeACL(self.tpg, wwn, mode='lookup')
@@ -870,14 +876,14 @@
     def ui_command_create(self, mapped_lun, tpg_lun_or_backstore, 
write_protect=None):
         '''
         Creates a mapping to one of the TPG LUNs for the initiator referenced
-        by the ACL. The provided I{tpg_lun_or_backstore} will appear to that
-        initiator as LUN I{mapped_lun}. If the I{write_protect} flag is set to
-        B{1}, the initiator will not have write access to the Mapped LUN.
+        by the ACL. The provided "tpg_lun_or_backstore" will appear to that
+        initiator as LUN "mapped_lun". If the "write_protect" flag is set to
+        1, the initiator will not have write access to the mapped LUN.
 
-        A storage object may also be given for the I{tpg_lun_or_backstore} 
parameter,
+        A storage object may also be given for the "tpg_lun_or_backstore" 
parameter,
         in which case the TPG LUN will be created for that backstore before
         mapping the LUN to the initiator. If a TPG LUN for the backstore 
already
-        exists, the Mapped LUN will map to that TPG LUN.
+        exists, the mapped LUN will map to that TPG LUN.
 
         Finally, a path to an existing block device or file can be given. If 
so,
         a storage object of the appropriate type is created with default 
parameters,
@@ -885,7 +891,7 @@
 
         SEE ALSO
         ========
-        B{delete}
+        delete
         '''
         self.assert_root()
         try:
@@ -963,11 +969,11 @@
 
     def ui_command_delete(self, mapped_lun):
         '''
-        Deletes the specified I{mapped_lun}.
+        Deletes the specified mapped LUN.
 
         SEE ALSO
         ========
-        B{create}
+        create
         '''
         self.assert_root()
         for na in self.rtsnodes:
@@ -1086,25 +1092,25 @@
                           add_mapped_luns=None):
         '''
         Creates a new LUN in the Target Portal Group, attached to a storage
-        object. If the I{lun} parameter is omitted, the first available LUN in
+        object. If the "lun" parameter is omitted, the first available LUN in
         the TPG will be used. If present, it must be a number greater than 0.
-        Alternatively, the syntax I{lunX} where I{X} is a positive number is
+        Alternatively, the syntax "lunX" where "X" is a positive number is
         also accepted.
 
-        The I{storage_object} may be the path of an existing storage object,
-        i.e. B{/backstore/pscsi0/mydisk} to reference the B{mydisk} storage
-        object of the virtual HBA B{pscsi0}. It also may be the path to an
+        The "storage_object" may be the path of an existing storage object,
+        i.e. "/backstore/pscsi0/mydisk" to reference the "mydisk" storage
+        object of the virtual HBA "pscsi0". It also may be the path to an
         existing block device or image file, in which case a storage object
         will be created for it first, with default parameters.
 
-        If I{add_mapped_luns} is omitted, the global parameter
-        B{auto_add_mapped_luns} will be used, else B{true} or B{false} are
-        accepted. If B{true}, then after creating the LUN, mapped LUNs will be
-        automatically created for all existing node ACLs, mapping the new LUN.
+        "add_mapped_luns" can be "true" of "false". If true, then
+        after creating the ACL, mapped LUNs will be automatically
+        created for all existing LUNs. If the parameter is omitted,
+        the global parameter "auto_add_mapped_luns" is used.
 
         SEE ALSO
         ========
-        B{delete}
+        delete
         '''
         self.assert_root()
 
@@ -1188,15 +1194,15 @@
 
     def ui_command_delete(self, lun):
         '''
-        Deletes the supplied LUN from the Target Portal Group. The I{lun} must
+        Deletes the supplied LUN from the Target Portal Group. "lun" must
         be a positive number matching an existing LUN.
 
-        Alternatively, the syntax I{lunX} where I{X} is a positive number is
+        Alternatively, the syntax "lunX" where "X" is a positive number is
         also accepted.
 
         SEE ALSO
         ========
-        B{create}
+        create
         '''
         self.assert_root()
         if lun.lower().startswith("lun"):
@@ -1303,9 +1309,9 @@
 
     def ui_command_create(self, ip_address=None, ip_port=None):
         '''
-        Creates a Network Portal with specified I{ip_address} and
-        I{ip_port}.  If I{ip_port} is omitted, the default port for
-        the target fabric will be used. If I{ip_address} is omitted,
+        Creates a Network Portal with the specified IP address and
+        port.  If the port is omitted, the default port for
+        the target fabric will be used. If the IP address is omitted,
         INADDR_ANY (0.0.0.0) will be used.
 
         Choosing IN6ADDR_ANY (::0) will listen on all IPv6 interfaces
@@ -1317,7 +1323,7 @@
 
         SEE ALSO
         ========
-        B{delete}
+        delete
         '''
         self.assert_root()
 
@@ -1379,11 +1385,11 @@
 
     def ui_command_delete(self, ip_address, ip_port):
         '''
-        Deletes the Network Portal with specified I{ip_address} and I{ip_port}.
+        Deletes the Network Portal with the specified IP address and port.
 
         SEE ALSO
         ========
-        B{create}
+        create
         '''
         self.assert_root()
         portal = NetworkPortal(self.tpg, self._canonicalize_ip(ip_address),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetcli/version.py 
new/targetcli-fb-2.1.51/targetcli/version.py
--- old/targetcli-fb-2.1.49/targetcli/version.py        2018-09-05 
14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/targetcli/version.py        2019-11-06 
13:31:26.000000000 +0100
@@ -15,4 +15,4 @@
 under the License.
 '''
 
-__version__ = '2.1.fb49'
+__version__ = '2.1.51'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetcli.8 
new/targetcli-fb-2.1.51/targetcli.8
--- old/targetcli-fb-2.1.49/targetcli.8 2018-09-05 14:08:11.000000000 +0200
+++ new/targetcli-fb-2.1.51/targetcli.8 2019-11-06 13:31:26.000000000 +0100
@@ -10,6 +10,11 @@
 files, volumes, local SCSI devices, or ramdisk, and export them to
 remote systems via network fabrics, such as iSCSI or FCoE.
 .P
+There is a daemon component for targetcli, which will greatly improve
+the overall execution time taken by targetcli commands at scale. For
+more details about switching to daemonized mode refer to targetclid(8)
+man page.
+.P
 The configuration layout is tree-based, similar to a filesystem, and
 is navigated in a similar manner.
 .SH USAGE
@@ -458,8 +463,9 @@
 .B /etc/target/backup/*
 .SH ENVIRONMENT
 .SS TARGETCLI_HOME
-If set, this variable points to a directory that should be used instead of 
~/.targetctl
+If set, this variable points to a directory that should be used instead of 
~/.targetcli
 .SH SEE ALSO
+.BR targetclid (8),
 .BR targetctl (8),
 .BR tcmu-runner (8)
 .SH AUTHOR
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/targetcli-fb-2.1.49/targetclid.8 
new/targetcli-fb-2.1.51/targetclid.8
--- old/targetcli-fb-2.1.49/targetclid.8        1970-01-01 01:00:00.000000000 
+0100
+++ new/targetcli-fb-2.1.51/targetclid.8        2019-11-06 13:31:26.000000000 
+0100
@@ -0,0 +1,77 @@
+.TH targetclid 8
+.SH NAME
+.B targetclid
+\- daemon component for targetcli
+.SH DESCRIPTION
+.B targetclid
+is the daemon component of targetcli, which will help retain state of various
+configfs object in memory, hence any new request/command can directly use the
+in memory objects instead of reconstructing them by parsing through the entire
+configfs files again and again for each and every single command. This will
+greatly improve the overall execution time taken by targetcli commands at 
scale.
+
+.SH USAGE
+.B targetclid [cmd]
+.br
+.B "--help"
+for additional usage information.
+.br
+.B "--version"
+for version information.
+.SH QUICKSTART & EXAMPLES
+.TP
+To start using the daemon, one need to enable targetclid socket,
+.br
+$ systemctl enable targetclid.socket
+.TP
+If you would like to use the daemonized approach as default method then,
+.br
+$ targetcli set global auto_use_daemon=true
+.br
+$ targetcli ls
+.TP
+You can use batch mode for sending multiple commands in one go,
+.br
+$ targetcli <hit-enter>
+.br
+targetcli shell version 2.1.50
+.br
+Entering targetcli batch mode for daemonized approach.
+.br
+Enter multiple commands separated by newline and type 'exit' to run them all 
in one go.
+.br
+/> ls
+.br
+/> pwd
+.br
+/> get global loglevel_file
+.br
+/> exit
+.br
+.TP
+You can set preference to stop using daemonized mode even when the daemon is 
not running,
+.br
+$ targetcli --disable-daemon
+.SH FILES
+.B /etc/target/saveconfig.json
+.br
+.B /etc/target/backup/*
+.br
+.B /var/run/targetclid.sock
+.br
+.B /var/run/targetclid.pid
+.SH ENVIRONMENT
+.SS TARGETCLI_HOME
+If set, this variable points to a directory that should be used instead of 
~/.targetcli
+.SH SEE ALSO
+.BR targetcli (8),
+.BR targetctl (8),
+.BR tcmu-runner (8)
+.SH AUTHOR
+Written by Prasanna Kumar Kalever <prasanna.kale...@redhat.com>
+.br
+Man page written by Prasanna Kumar Kalever <prasanna.kale...@redhat.com>
+.SH REPORTING BUGS
+Report bugs via <targetcli-fb-de...@lists.fedorahosted.org>
+.br
+or <https://github.com/open-iscsi/targetcli-fb/issues>

++++++ targetclid.service ++++++
[Unit]
Description=Targetcli daemon
Documentation=man:targetclid(8)
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/targetclid
Restart=on-failure

[Install]
WantedBy=multi-user.target
Also=targetclid.socket
++++++ targetclid.socket ++++++
[Unit]
Description=targetclid socket
Documentation=man:targetclid(8)

[Socket]
ListenStream=/var/run/targetclid.sock

[Install]
WantedBy=sockets.target

Reply via email to