diff --git a/src/color_ssh/__init__.py b/src/color_ssh/__init__.py
index b794fd4..df9144c 100644
--- a/src/color_ssh/__init__.py
+++ b/src/color_ssh/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '0.1.0'
+__version__ = '0.1.1'
diff --git a/src/color_ssh/color_cat.py b/src/color_ssh/color_cat.py
index a9d1412..036ced8 100644
--- a/src/color_ssh/color_cat.py
+++ b/src/color_ssh/color_cat.py
@@ -68,8 +68,9 @@ def main(argv=sys.argv, stdin=io2bytes(sys.stdin), stdout=io2bytes(sys.stdout),
     """
     setting = Setting().parse_args(argv, stdout)
 
-    # Note: Do not use 'fileinput' module because it causes a buffering problem.
-    try:
+    @exception_handler(lambda e: stderr.write(('%s: %s\n' % (e.__class__.__name__, e)).encode('utf-8', 'ignore')))
+    def f():
+        # Note: Do not use 'fileinput' module because it causes a buffering problem.
         for path in setting.paths:
             fh = stdin if path is None else io.open(path, 'rb', 0)
             try:
@@ -79,9 +80,6 @@ def main(argv=sys.argv, stdin=io2bytes(sys.stdin), stdout=io2bytes(sys.stdout),
             finally:
                 if fh is not stdin:
                     fh.close()
+        return 0
 
-    except Exception as e:
-        stderr.write(('%s: %s\n' % (e.__class__.__name__, e)).encode('utf-8', 'ignore'))
-        return 1
-
-    return 0
+    return f()
diff --git a/src/color_ssh/color_ssh.py b/src/color_ssh/color_ssh.py
index f8d82d8..1d2e798 100644
--- a/src/color_ssh/color_ssh.py
+++ b/src/color_ssh/color_ssh.py
@@ -106,7 +106,12 @@ def run_task(args):
 
     prefix = ['color-cat', '-l', label]
 
-    try:
+    def exc_func(e):
+        msg = '%s: %s\nlabel=%s, command=%s\n' % (e.__class__.__name__, e, label, command)
+        stderr.write(msg.encode('utf-8', 'ignore'))
+
+    @exception_handler(exc_func)
+    def f():
         proc_stdout = subprocess.Popen(prefix, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
         proc_stderr = subprocess.Popen(prefix + ['-s', '+'], stdin=subprocess.PIPE, stdout=stderr, stderr=stderr)
         ret = subprocess.call(command, stdin=None, stdout=proc_stdout.stdin, stderr=proc_stderr.stdin)
@@ -116,11 +121,9 @@ def run_task(args):
 
         proc_stdout.wait()
         proc_stderr.wait()
-    except Exception as e:
-        msg = '%s: %s\nlabel=%s, command=%s\n' % (e.__class__.__name__, e, label, command)
-        stderr.write(msg.encode('utf-8', 'ignore'))
-        return 1
-    return ret
+        return ret
+
+    return f()
 
 
 def main(argv=sys.argv, stdout=io2bytes(sys.stdout), stderr=io2bytes(sys.stderr)):
@@ -128,7 +131,8 @@ def main(argv=sys.argv, stdout=io2bytes(sys.stdout), stderr=io2bytes(sys.stderr)
     Main function
     """
 
-    try:
+    @exception_handler(lambda e: stderr.write(('%s: %s\n' % (e.__class__.__name__, e)).encode('utf-8', 'ignore')))
+    def f():
         setting = Setting().parse_args(argv, stdout)
         n = min(len(setting.tasks), setting.parallelism)
         if n <= 1:
@@ -136,9 +140,6 @@ def main(argv=sys.argv, stdout=io2bytes(sys.stdout), stderr=io2bytes(sys.stderr)
         else:
             pool = Pool(n)
             ret = pool.map(run_task, setting.tasks)
-    except Exception as e:
-        msg = '%s: %s\n' % (e.__class__.__name__, e)
-        stderr.write(msg.encode('utf-8', 'ignore'))
-        return 1
+        return max(ret)
 
-    return max(ret)
+    return f()
diff --git a/src/color_ssh/util/util.py b/src/color_ssh/util/util.py
index e1cdb8a..8bdea63 100644
--- a/src/color_ssh/util/util.py
+++ b/src/color_ssh/util/util.py
@@ -3,7 +3,7 @@ from __future__ import division, print_function, absolute_import, unicode_litera
 import sys
 import os
 
-__all__ = ['PY3', 'arg2bytes', 'io2bytes', 'distribute']
+__all__ = ['PY3', 'arg2bytes', 'io2bytes', 'distribute', 'exception_handler']
 
 PY3 = sys.version_info >= (3,)
 
@@ -37,3 +37,24 @@ def distribute(num_workers, tasks):
         ret.append(tasks[j:j + k])
         j += k
     return ret
+
+
+#
+# Decorators
+#
+def exception_handler(exception_func):
+    def f(func):
+        import functools
+
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            try:
+                return func(*args, **kwargs)
+            except KeyboardInterrupt:
+                return 130
+            except Exception as e:
+                exception_func(e)
+                return 1
+
+        return wrapper
+    return f

Kill all processes when interrupted by keyboard
diff --git a/tests/color_ssh/util/__init__.py b/tests/color_ssh/util/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/color_ssh/util/test_util.py b/tests/color_ssh/util/test_util.py
new file mode 100644
index 0000000..50082dc
--- /dev/null
+++ b/tests/color_ssh/util/test_util.py
@@ -0,0 +1,13 @@
+from __future__ import division, print_function, absolute_import, unicode_literals
+
+from mog_commons.unittest import TestCase
+from color_ssh.util.util import exception_handler
+
+
+class TestUtil(TestCase):
+    def test_exception_handler(self):
+        @exception_handler(lambda e: e)
+        def f():
+            raise KeyboardInterrupt
+
+        self.assertEqual(f(), 130)

pip install -e .
parse_log_pytest
requirements.txt
pytest pep8 coverage six mog-commons
apt-get update apt-get install -y gcc
3.5
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
-e git+https://github.com/mogproject/color-ssh.git@9adb19916b0205fd6a88beddcd8669114edc449c#egg=color_ssh
coverage==6.2
importlib-metadata==4.8.3
iniconfig==1.1.1
Jinja2==3.0.3
MarkupSafe==2.0.1
mog-commons==0.2.3
packaging==21.3
pep8==1.7.1
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: color-ssh
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - coverage==6.2
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - jinja2==3.0.3
      - markupsafe==2.0.1
      - mog-commons==0.2.3
      - packaging==21.3
      - pep8==1.7.1
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/color-ssh

tests/color_ssh/util/test_util.py::TestUtil::test_exception_handler
src/color_ssh/util/util.py src/color_ssh/color_ssh.py src/color_ssh/color_cat.py src/color_ssh/__init__.py
src/color_ssh/util/util.py src/color_ssh/color_ssh.py src/color_ssh/color_cat.py src/color_ssh/__init__.py