SharpZipLibの日本語対応

SharpZipLib 1.2.0以前でtarファイルを作成すると日本語が文字化けしていたけど、何時の間にか日本語に対応していたんですね。TarOutputStreamのConstructorでEncodingを指定できるではないですか。

github(https://github.com/icsharpcode/SharpZipLib)から最新のソースコードをダウンロードしてきて、自らビルドしましょう。

WevDAV上のExcelファイルを更新すると”アップロードできませんでした この場所の保存内容をアップロードするには、サインインする必要があります。”のエラーになる

QNAPのNASの機能を使ってWevDAVの共有フォルダを作成しました。共有フォルダ上のファイルををOffice 2016で開くとファイル更新時に「アップロードできませんでした この場所の保存内容をアップロードするには、サインインする必要があります。」と言うエラーが発生します。調べたところなかなか根の深い問題で、どうにもMicrosoft Officeアップローダーの仕様上の問題に起因するようです。

ちなみにWevDAV共有フォルダ上のファイルをエクスプローラーで操作したり、Office以外のアプリケーションから読み書きする場合には問題がありません。

Microsoft OfficeアップロードセンターはOffice 2013以降に追加された機能です。通信が不安定な環境において、更新されたWebDAV上のファイルをキャッシュして、非同期的にWebDAVサーバーに送信する機能を提供しています。MicrosoftはOneDrive上のOfficeファイルを開くときに、Microsoftが独自に認証周りを拡張したWebDAVを使用しています。OneDriveのファイルをOfficeで直接扱うためのモジュールとして広く使われています。この独自に拡張しているというところが曲者で、Windows統合認証やMicrosoft Liveアカウント認証に対応していないサーバーの場合に、認証に失敗してしまうようなのです。

Windows 10の場合はタスクトレイに常駐しているMicrosoft OneDrive設定から「ファイルのコラボレーション」を無効にすると、Microsoft Officeアップローダーを止められるようです。Googleから”UPLOAD FAILED: You are required to sign in to upload your changes to this location”で検索すると色々と情報が見つかりますが、OfficeやOSのバージョンによって設定すべき項目が異なったり、公式な対策でもないようなのでなかなか面倒そうです。

この辺りまで調べたところで、WebDAVの使用はあきらめる事にしました。

参考:UPLOAD FAILED: You are required to sign in to upload your changes to this location

letsencryptの証明書更新で”‘Namespace’ object has no attribute ‘standalone_supported_challenges'”が発生する

OSをUbuntu 18.04LTSからUbuntu 20.04LTSに移行したところ、旧システムから移行してきた証明書の更新に、以下のようなエラーを表示して失敗しました。

> sudo letsencrypt renew
...
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/www.example.net.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Renewal configuration file /etc/letsencrypt/renewal/www.example.net.conf (cert: www.example.net) produced an unexpected error: 'Namespace' object has no attribute 'standalone_supported_challenges'. Skipping.
...

letsencryptのバージョンが上がったことによりstandalone_supported_challengesオプションがサポートされなくなったことが原因です。

/etc/letsencrypt/renewal/www.example.net.confを編集して、最後の行のstandalone_supported_challengesをコメントアウトすると正常に動作するようになりました。

# renew_before_expiry = 30 days
cert = /etc/letsencrypt/live/www.example.net/cert.pem
privkey = /etc/letsencrypt/live/www.example.net/privkey.pem
chain = /etc/letsencrypt/live/www.example.net/chain.pem
fullchain = /etc/letsencrypt/live/www.example.net/fullchain.pem
version = 0.31.0
archive_dir = /etc/letsencrypt/archive/www.example.net

# Options and defaults used in the renewal process
[renewalparams]
authenticator = standalone
account = 52bc4f19233c146191f7f576945ed0d9
server = https://acme-v01.api.letsencrypt.org/directory
# standalone_supported_challenges = "tls-sni-01,http-01"

参考:https://github.com/certbot/certbot/issues/6984

C:\Windows\Tempにイベントログファイルが作成される。

C:\Windows\Tempにイベントログファイルが作成され、C:ドライブの空き容量が無くなる。

設定→システム→記憶域で見ると一時ファイルが多くの容量を使用しているが、一時ファイルの内容を確認してみると明細の合計が、一時ファイルで使用している容量よりもはるかに小さい。また一時ファイルの削除を行ってもC:ドライブの空き容量が回復しない。

実際に何処のフォルダにファイルが作成されて居るのか確認したところ、C:\Windows\Tempフォルダに大量のファイルが作成されて居ることを確認できた。どうやらC:\Windows\Temp以下のフォルダは自動では削除されないらしい。

本来ならイベントログファイルはC:\Windows\System32\winevt\Logsに作成される。検索してみたところ”Low Disk Space error due to a full Temp folder“が見つかった。どうもWindows UpdateやMicrosoft Storeの障害にともなう不具合のようである。上記の手順でトラブルシュート後にWindows Updateを適用する事で解決した。

C:\Windows\Tempにイベントログファイルが作成される。

C:\Windows\Tempにイベントログファイルが作成され、C:ドライブの空き容量が無くなる。

設定→システム→記憶域で見ると一時ファイルが多くの容量を使用しているが、一時ファイルの内容を確認してみると明細の合計が、一時ファイルで使用している容量よりもはるかに小さい。また一時ファイルの削除を行ってもC:ドライブの空き容量が回復しない。

実際に何処のフォルダにファイルが作成されて居るのか確認したところ、C:\Windows\Tempフォルダに大量のファイルが作成されて居ることを確認できた。どうやらC:\Windows\Temp以下のフォルダは自動では削除されないらしい。

本来ならイベントログファイルはC:\Windows\System32\winevt\Logsに作成される。検索してみたところ”Low Disk Space error due to a full Temp folder“が見つかった。どうもWindows UpdateやMicrosoft Storeの障害にともなう不具合のようである。上記の手順でトラブルシュート後にWindows Updateを適用する事で解決した。

テレワーク実施率5.6%は本当に少ないのか

テレワーク実施率5.6%のニュースをみて、余り物少なさに驚くような論調の記事が散見されるが本当に少ないのだろうか?「少ない」というからには何かに比較して少ないと言うのだろうが、その何かを明確にして論じている人は殆どいない。比較対照を持たずに「少ない」と論ずるのは、単なる主観でしかないので気をつけよう。

テレワークを実現できるか否かは職種によって大きく異なる。職種別労働人口に関する資料は無いかと検索したところ独立法人 労働政策研究・研修機構の2019年度職業別就業者数が見つかった。

上記資料では事務従事者、専門的・技術的従事者、生産工程従事者、販売従事者、サービス職業従事者、運搬・清掃・包装等従事者、建設採掘従事者、輸送・機械運転従事者、農林漁業従事者、保安職業従事者、管理的職業従事者、分類不能の12に分類して集計している。このうちテレワークが出来そうな職業は事務従事者(19.6%)、専門的・技術的従事者(17.5%)、管理的職業従事者(1.9%)程度しか見当たらない。これら3職業の合計で全体の39%ですかない。この専門的・技術的従事者には情報産業以外も含まれているはずだ。

検索したところ社会実績データ図録の技術者数の推移が見つかった。2015年時点で情報通信作業に従事する技術者は、技術者全体の42%となる。

テレワークできる職業に従事しているのは、ここまでの数字から最大に見積もっても29.4%でしかない。情報通信作業のうちインフラ系に属する人はテレワークでは対処できないので、実際にはもっと少ないはずだ。

詰まるところテレワーク実施率5.6%と言うが、テレワーク可能な業種で就労する人の19.0%以上はテレワークに移行していると推測できる。これが多いか少ないかは論じない。だが予想外に小さいと思うような数字ではないと思う。

アパレル系は休業するか考え直して!

非常事態宣言を受けてアパレル系が休業を決めているようだが、ちょっと考え直して欲しい。衣類は”衣食住”の一角を占める重要な生活消耗品です。せめて下着や子供服売場は開けてください。

入院するとなれば下着やパジャマ、タオルを買い足さざる得ないときもある。
子供服なんて去年の着れないと言うこともある。
乳児が生まれるれば産着を揃える必要もある。
しかも今は季節の変わり目。

各事業者には、自社の提供するサービスの社会的意義をしっかり振り返って、休業するべきか否か判断して欲しいです。
https://www.nikkei.com/article/DGXMZO57805630Y0A400C2000000/

経済対策で消費税減税とかあり得ないよと言う話し

消費税現在が叫ばれているが、消費税を勘違いしている人が存外多いので、簡単に纏めておく。

消費税に良くある誤解

企業は消費税をほとんど負担していない

企業も物を仕入れたり、経費をつかったりすると、消費税を支払います。例えば900万円で仕入れたなら、消費税90万円を含む990万円を支払うこととなる。この時の消費税90万円を、経理上は仮払消費税として別途集計しておきます。

逆に企業が物を売ったりすると、消費税を受け取ります。例えば1000万円の売上なら、消費税100万円を加えて、1100万円を受け取る。この時の消費税100万円を、経理上は仮受消費税として別途集計しておきます。

そして決算時に仮受消費税の合計額から、仮払消費税の合計額を引いた、残りを消費税額を消費税納税額として申告します。つまり仮払消費税が90万円、仮受け消費税が100万円なら、納税する消費税は10万円です。この納税する分の10万円は一括で支払う訳では無く、翌年に月々分割で納税していうことになります。

このように消費税は最終消費者が10%の消費税を負担する仕組みになっています。企業は経費として一旦は仮払消費税を払いますが、最後はお客様から頂いた仮受消費税と相殺します。したがって企業側は消費税負が殆ど無い(交際費など一部の経費は消費税を負担している)のです。

取引の度に消費税が積み上がるので、最終消費者の実質負担は10%ではなく数十%になる・・・と言う誤解を時折見かけます。そんな事は無いのです。

税率が変わった後も旧税率での売上や支払が起こる

税率の変更日以降の取引において消費税率が変わります。逆に言えば、変更日以前に1年単位あるいはもっと長期にわたる契約を取り交わしていた場合には、変更日以後も旧税率での取引が発生します。良くあるのは年単位の保守契約や賃借契約で代金を前払いしている場合です。長い場合には10年以上にわたって旧税率での取引が残ることもあります。(※実際には様々な特例規定が設けられてます。)

そのため企業の情報システムは税率変更後も旧税率の取引を、今後何年間にもわたって取り扱えなくてはなりません。場合によっては旧々税率の取引も扱う必要があります。だからこそ多大なシステム改修が発生するのです。

レジの消費税率変更はマニュアルを見れば誰でも簡単にできる・・・と言われる方がいますが、レジの消費税率設定は、消費税にかかわるシステムの一部に過ぎないのです。

税率変更に伴う価格改定作業は意外と時間がかかる

国が総額表示(内税価格での表示)を義務づけています。そのために店頭の価格表示は原則として総額表示です。税率改定時には旧税率総額表示になっている価格を全て外税表示に差し替え、税率変更日以後に改めて新税率の総額表示に差し替えるという作業をおこなっています。

食品スーパーなどはオンラインで表示価格を変更できる電子棚札を用いていますが、それ以外の小売業は紙に印刷していることが殆どです。これらを全て差し替えるには、仕事の合間を縫いながら何週間もかけて実施しています。

消費税減税は増税よりも難しい

消費税減税の告知により買い控えが発生する

消費税率変更にはシステム改修を避けられません。そのため、消費税率を下げる場合でも数年前からの告知が必要不可欠です。数年後に消費税率が大幅に下がるとなれば、広範囲で買い控えが発生するのを避けられません。

地方税収入が減額される

消費税10%のうち、7.8%が国税、2.2%は地方税となっています。地方自治体の税収源は限られており、また国債の発行などにより赤字を埋め合わせるのかも難しいのです。予算的な埋め合わせを考えずに消費税減税だけを実施すれば、元々余裕のない地方自治体の財政が破綻する可能性もあります。

上記のことを踏まえると、仮に消費税減税を実施するにしても「20年計画で2年ごとに1%づつ下げる」ような非常にゆっくりとした減税にするほか無く、緊急の経済対策として機能しうる物では無いです。

もし経済対策として行える可能性があるとしたら、消費税率を一度にゼロまで下げる事です。殆どのシステムは非課税(税率ゼロ)の取引を扱うことが出来るはずです。したがって税率をゼロにするならシステム改修は数ヶ月で済むと思われます。ただし、その数ヶ月間は個人消費が恐ろしく落ち込むので、同時に相当の財政出動を行わないと、多くの企業が破綻するでしょうけど。

.NET COREでLinuxで動作するサービスを作成する

.NET Core + C#でLinuxのDeamon作れないかなと思ったら、思ったより簡単に作れるようになっていたので覚え書き。Program.csとDaemonService.csの中味はほぼ定型なのでサンプルからそのまま流用します。

以下のサンプルは.NET Core 3.1で記述しています。

// Program.cs

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace DaemonSample
{
    class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = new HostBuilder()
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.AddEnvironmentVariables();

                    if (args != null)
                    {
                        config.AddCommandLine(args);
                    }
                })
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddOptions();
                    services.Configure<DaemonConfig>(hostContext.Configuration.GetSection("Daemon"));

                    services.AddSingleton<IHostedService, DaemonService>();
                })
                .ConfigureLogging((hostingContext, logging) => {
                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    logging.AddConsole();
                });

            await builder.RunConsoleAsync();
        }
    }
}
// DaemonService.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace DaemonSample
{
    class DaemonService : IHostedService, IDisposable
    {
        private readonly ILogger _logger;
        private readonly IOptions<DaemonConfig> _config;
        public DaemonService(ILogger<DaemonService> logger, IOptions<DaemonConfig> config)
        {
            _logger = logger;
            _config = config;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Starting daemon: " + _config.Value.DaemonName);
            _logger.LogInformation("arg1:{0}", _config.Value.arg1);
            _logger.LogInformation("arg2:{0}", _config.Value.arg2);

            // ここでサーバー本体の処理をTaskとして起動します。

            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Stopping daemon.");
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _logger.LogInformation("Disposing....");
        }
    }
}
// DaemonConfig.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace DaemonSample
{
    class DaemonConfig
    {
        public string DaemonName { get; set; }
        public string arg1{ get; set; }
        public string arg2{ get; set; }
    }
}

起動時のコマンドラインは下記の通り。コマンドライン引数の受け渡し方が非常に独特です。

/usr/bin/dotnet run --project deamonsample --configuration Release --Daemon:DaemonName="Sample Daemon" Daemon:arg1="argument 1" Daemon:arg2="argument 2"

Demonとして起動するにはsystemdを使用します。/etc/systemd/systemにdeamonsample.serviceファイルを作成し、下記の様に記述します。

[Unit]
Description=deamon sample
After=network.target

[Service]
ExecStart=/usr/bin/dotnet run --project atozipsrv --configuration Release --Daemon:DaemonName="deamonsample " Daemon:arg1="argument 1" Daemon:arg2="argument 2"
WorkingDirectory=/usr/src/deamonsample
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=atozip
Environment=DOTNET_CLI_HOME=/var/src/deamonsample

[Install]
WantedBy=multi-user.target

後は通常のsystemdのDeamon同様に以下のコマンドで起動します。

$ systemctl start deamonsample

参考:Creating a Daemon with .NET Core (Part 1)

QNAP:他のNASから同期したフォルダに書込できない

HBS3 Hybrid Backup Syncの同期機能を使って既存のNAS(SMB)からQNAPにファイルをレプリケーションしておいてサーバーの移行を行い、いざ切替というところで共有フォルダ内にあるフォルダ以下に書き込むことが出来ないというトラブルが発生。共有フォルダのルートには書き込めるので、チェック時点では気がつかなかったのです。

Windows上からフォルダのプロパティを見るとAdministratorにのみ書込権限があり、Evryoneでは書き込めない状態になっています。

QNAP管理画面のFileStationからフォルダのプロパティを見ると所有者には書込権限はありますが、グループとパブリックには書込権限がありません。ここでグループとパブリックにも書込権限を付与すると、Windows上からファイルをコピーできるようになるので、これが原因のようです。

さて、困ったことにFileStationから複数のフォルダを選択して権限設定を書き換えることは出来ません。フォルダの数が多く、フォルダ毎に手作業で行うとなると、相応に時間がかかります。

ふと、QNAPのNASはsshで接続出来ることを思い出しました。コントロールパネルの「ネットワークサービスとファイルサービス」から「Telnet/SSH」を選択して「SSH接続を許可する」にチェックを付けます。

QNAPの共有フォルダは/share以下に作成されます。以下のコマンドを実行して全てのユーザーに書込権限を付与します。

chmod -R a+rw /share/Public

NASにSSHで入れると便利だね。

ちなみに環境は以下の通り。
QNAP TS-351
Firmware Version 4.4.1
HBS3 version 3.0.191202