2021/05/07

PHP7以前 クエリ結果のカラム文字列を取得できない場合(2GB文字列問題)

データベースのカラムからデータを取得し、phpで扱う作業を行った際、
文字列を取得してこなくて、酷く嵌ったので、オボエガキ。

PHP7とPHP5で平行稼働させている類似のプログラムがあり、
先にPHP7を修正し、追加のカラムの文字列を取得させると、意図する通りに動作した。

続いてPHP5を動かすが、 取得してこない。

 

色々調べたところ、PHP(7以前)の文字列型は2GB以上の文字列を処理できない
正確には、nverchar等でカラムサイズが2147483647(int4の最大値)
で定義されている場合、PHP内でうまく扱えず、空白として処理されてしまうようだ。

PHPのzvalue_value共用体で文字列長をint型で管理しているため、
文字列の長さが2147483647を超えるとマイナスになってしまうことが原因らしい

PHP5のまま稼働させる必要があったので、クエリにてレコードを取得する際に、

select CONVERT(VARCHAR(512), <カラム名>) as hoge from <table名> where ~;

 

のように、varcharのサイズを指定してカラムを取得すれば,以降PHP内でカラムの文字列を扱えた。

3時間くらい同僚と嵌ってしまい、困ったので、忘れないように残しておく。



2021/03/02

rapidSSL サーバ証明書更新のオボエガキ

前提
opensslコマンドがつかる環境である事

申込後
WEBサイトから更新の申し込みを行い、その際取得した、CSRとKEYをテキストファイルに
貼り付け、 key.txt と csr.txt 等の名前で作業ディレクトリに保存 
key.txtをサーバにアップするため、RSA形式に変換


# openssl rsa -in key.txt -out <サーバにアップする際の名前>.key
# openssl rsa -in key.txt -out hoge_2021rsa.key
(今回は仮として、hoge_2021rsa.key とする)
-----BEGIN RSA PRIVATE KEY----- から
-----END RSA PRIVATE KEY-----  のRSA形式の秘密鍵に変換できる。

中間証明書をrapidSSLのサイトから取得
https://www.geotrust.co.jp/resources/rapidssl/repository/intermediate_sha2.html
ここが正規の所みたいだが、RapidSSLの人は、デジサートのアカウントじゃないから、
2021年の3月以降は、このページではサポートしないようである。
3月以降下のURLにリダイレクトさせると記載されている。
https://knowledge.digicert.com/ja/jp/solution/SOT0018.html?_ga=2.199517224.764125246.1614230988-505583794.1614230987
リンク先にある、テキストフィールドの
「—–BEGIN CERTIFICATE —–」から
「—–END CERTIFICATE—–」をテキストに貼り付けて、
中間証明書のcrtファイルとして保存する。(仮にintermediate_2021.crt とする)

 2021-03現在、crtファイルの生成に、DNS テキストフィールドを利用して
認証させる方法をrapidSSLが採用しているので、
サーバのnamed.serviceの設定ファイルにTXTレコードを追記し、
named.serviceを再起動させる。

DNSサーバツールであるbindがインストールされているはずだが、
無い場合は、インストールしておく。弊社はbindで動いている。
# yum install bind
# yum install bind-utils

設定ファイルは以下に保存されているはずなので、

/etc/named.conf の中に記載の ディレクトリにあるdbに追記TXTレコードを追記

・・・
options {
//listen-on port 53 { 127.0.0.1; };
listen-on port 53 { 192.168.142.31; };
listen-on-v6 port 53 { ::1; };
directory  "/var/named";
dump-file  "/var/named/data/cache_dump.db";
・・・


上記の場合、cache_dump.db に記載すれば良い

TXTレコードは

ドメイン名   :hoge.jp
ホスト名    : 
レコードタイプ : TXT
指定内容(Value):
1234567890abcdef(rapidsslから指示がある文字列)

例: hoge.jp.   IN TXT "
1234567890abcdef"

複数行ある場合は、カンマ区切りで一行に書くこと
(複数行で記載すると、最初の一行だけ反映される仕様のようだ)

DNSサーバ再起動後、30分くらいでTXTレコードが拾えるようになるはず。

googleさんにお願いしてhoge.jpのTXTレコードを照会する場合
$ dig @8.8.8.8 hoge.jp TXT 
;; ANSWER SECTION:
hoge.jp.         2xxxx   IN      TXT     "
1234567890abcdef"

こう帰ってくれば、反映されている。

この状態で、
Digicert社(rapidsslの親会社)が定期的にDNSをチェックしている際に、
上手く
TXTレコードに指定の文字列が確認が取れると、承認作業が完了し、Digicert社の審査を経て証明書発行される仕組み。

証明書がメールで送られてくるので、
「—–BEGIN CERTIFICATE —–」から
「—–END CERTIFICATE—–」をテキストに貼り付けて、crtファイルとして保存する。
(今回は、仮に
hoge_2021.crt とする。)

続いて、ssl証明書の配置をする。

設定ファイルは
/etc/httpd/conf.d/ssl.conf
に配置されているので、中身を確認し必要であれば、修正後のファイル名に置き換え等の
作業を行い、指定のディレクトリにRSA形式の秘密鍵と中間証明書.crt、証明書.crt
ファイルを配置した状態(ssl.confの中以下の記載のアドレスにファイルを配置)
#   Server Certificate:
SSLCertificateFile /usr/local/ssl/crt/hoge_2021.crt
#   Server Private Key:
SSLCertificateKeyFile /usr/local/ssl/private/hoge_2021rsa.key
#   Server Certificate Chain:
SSLCertificateChainFile /usr/local/ssl/crt/intermediate_2021.crt

上記のような状態でhttpdを再起動すれば、反映される(はず)
再起動
# systemctl restart httpd



2021/01/26

PHP XMLファイルからデータ取得

PHPにてXML形式のファイルから必要なデータを取得する。

予めssh2が使えるように、
libssh2とssh2のextensionを PHP7にインストールしておく。

#yum install gcc glibc-devel zlib-devel openssl-devel
#yum install --enablerepo=epel,remi,remi-php73 php-devel

本家LIBSSH2サイトへアクセスしwgetで取得
# wget https://www.libssh2.org/download/libssh2-1.9.0.tar.gz
# tar vxzf libssh2-1.9.0.tar.gz
# cd libssh2-1.9.0
# ./configure
# make
# make install

ライブラリ PCELから ssh2のモジュールを取得
# wget https://pecl.php.net/get/ssh2-1.2.tgz
# tar vxzf ssh2-1.2.tgz
# cd ssh2-1.2
# phpize
# ./configure --with-ssh2
# make
# make install

extensionに認識
php.iniに
extension=ssh2.so を記述し、apacheを再起動。

<?php
$filepath="<取得先のxmlファイルのアドレスを指定>";
//SCPでファイル要求
$connection = ssh2_connect('192.168.0.xxx', 22);//22ポートは開放しておく
ssh2_auth_password($connection, '<ユーザ名>', '<パスワード>');
$get_add = "/var/www/html/xml/get.xml";//一旦ローカルに取得
ssh2_scp_recv($connection, $filepath, $get_add);
//file_existsを使い、xmlがあればデータを取得させる
if(file_exists("/var/www/html/xml/get.xml")){
$xml=simplexml_load_file("/var/www/html/xml/get.xml");

//xmlの中身を取得 階層が深い時は、->で階層を深く指定すれば取得できる。
//xmlの構成を以下とした場合
//<address>
//  <zip>100-0001</zip>
//  <add>東京都何某</add>
//</address>

$column_xml = ($xml->address->zip);//zipの中身を取得
echo "取得したxmlのデータ⇒".$column_xml;
}else{
echo "xml取得失敗";
}
//ディレクトリに残存する当該xmlファイルを削除する
array_map('unlink', glob("/var/www/html/xml/get.xml"));
?>

 

2021/01/21

シェル(bash) データベースへのレコードを自動挿入

指定の時間に成ったら、レコードを挿入するためのシェル(bash)

#!/bin/bash
#postgresqlへアクセスし、データを書き込む
psql "postgresql://<ユーザ>:<パスワード>@192.168.0.xxx/<データベース名>" << _EOF
insert into table1 (create_date)
select to_number(to_char(now(), 'YYYYMMDD'),'99999999')
where NOT EXISTS (select 1 from table1 where create_date=to_number(to_char(now(), 'YYYYMMDD'),'99999999'))
_EOF

exit $?


データベースへの格納条件
create_dateは20210121のnumeric型

create_dateに今日の日付が入っているものが存在しなければ、
create_dateに今日の日付「YYYYMMDD」のnumeric型のデータでそのほかはデフォルト値
のレコードを挿入する。
存在すれば、レコードを挿入しない。

これをcronやRUNDECK等を使って指定の時間にシェルを走らせればいい。

Postgresql データ型変換 char numeric date関連

日付の取り扱いでよく使うもののオボエガキ

【1】今日の日付をnumericに20210121に形で格納したい場合
  now()をcharでYYYYMMDD型に加工した後に、numberで8桁の数値に変えればよい
結果  to_number(to_char(now(), 'YYYYMMDD'),'99999999')

 

【2】8桁の数字20210121をタイムスタンプ型にしたい場合
結果 to_timestamp(to_char(’20210121’,'0000-00-00'),'YYYY-MM-DD'))

 

【3】2021-01-21 12:00:00.0 を 2021-01-21のタイムスタンプ型で扱いたい場合
結果 to_timestamp(to_char(’2021-01-21 12:00:00.0’,'YYYY-MM-DD'),'YYYY-MM-DD')

 

※【2】と【3】は同じタイムスタンプ型になっているので、
where等で 【2】-【3】> INTERVAL '3 days' 等で経過日数を条件としたり出来る。

 

to_charの扱いとして
to_char(1111,'99999') ⇒ ' 1111' 先頭に空白を含み、指定桁数無視
to_char(1111,'FM99999') ⇒ '1111' 空白なし指定桁数無視
to_char(1111,'00000') ⇒ ' 01111' 先頭に空白と含み指定桁数のゼロを付加
to_char(1111,'FM00000') ⇒ '01111'  空白なし指定桁数のゼロ付加

となる FMをつけないと格納できないようなDBもあり得る。

2020/08/19

centos7 sambaサーバにcifsでマウントする

windowsユーザの為に、samba

接続したい名前を検索する。

 # smbclient -L 192.168.0.xxx -U%


        Sharename       Type      Comment
        ---------       ----      -------
        info            Disk      hogehoge Utilities
        share           Disk      hogehoge
        share2          Disk
        share3          Disk
        IPC$            IPC       IPC Service (hogehoge)
Reconnecting with SMB1 for workgroup listing.

        Server               Comment
        ---------            -------

        Workgroup            Master
        ---------            -------
        WORKGROUP            hogeFile


マウントしたい先は、//192.168.0.xxx/share2 と分かった。

# mount //192.168.0.2/share2 /home/hoge/mnt/ -o username=<sambaの登録ユーザ名>,password=<その人のパスワード>

 マウント先でシンボリックリンクを有効にしたい際は、

 # mount //192.168.0.2/share2 /home/hoge/mnt/ -o username=<sambaの登録ユーザ名>,password=<その人のパスワード>,mfsymlinks

と末尾に ,mfsymlinksのオプションを付加すること。

 接続先のcifsのヴァージョン古い場合は、ver2.0等の追記が必要とのことだが、

デフォルト値で接続できたので、このまま運用する。

 接続解除

# umount /home/hoge/mnt

 

 

ディレクトリのパーミッション一括変更処理

新たなユーザと新たなグループを作成し、既存のディレクトリのパーミッションを
数百個一括で変換したい事案が出たので、オボエガキ

findを使ってディレクトリを絞ってパイプでxargsに投げる方法

 

# find /home/hogehoge/test -type d -user nobody -perm 0755 -print | xargs chmod 644


『/home/hogehoge/test』配下にある『nobodyユーザ』の
パーミッション『0755』のディレクトリを『フルパス形式の標準出力』にして
『パイプ』以降の 『xargs』コマンドを使って『フルパス形式の標準出力』に対して、
パーミッション『644』を定義する。

findコマンド
find <処理したいディレクトリの先頭> <オプション>
オプション
-type d ディレクトリファイルを検索
-user nobody ユーザnobodyのファイルのみ検索
-perm 0755 パーミッションが755のものを検索
-print 見つけたファイルをフルパスで出力

| xargs chmod 644
パイプ『|』の右側へ左側のコマンドを投げる
xargs 標準入力やファイルからリストを読み込み、コマンドラインを作成して実行
chmod 644 パーミッションを644に変更

同じような操作方法として、パイプを使わずに パイプ以降を
-exec chmod 644 {} + と記述する方法もあるが、

xargsよりもexecのほうが処理の手順が多く、ファイル数が多くなるほど負荷がかかるため、
基本はxargsを推奨とのこと。
コマンドの分かりやすさからも、xargsのほうがよさそう。

2020/07/28

phpmailer のSMTP送信で嵌った際の対処

表題に行きつくまで2時間ほど嵌ったのでオボエガキ

以前のphp5.4.16では何の問題もなく動いていたのだが
新しいサーバがPHP 7.2.21で稼働している。

PHP5.6から、verify_peerのデフォルトがtrueになったそうで、
SMTP認証をする際に、証明書を求められてエラーが返ってくる。
自己署名証明書も駄目っぽいので、証明書の設定をしないでSMTPを使いたいとき

verify_peerを使いませんと宣言すればよい
と先人は簡単に仰っていたが、ここに行きつくまで2時間かかった・・・。

$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);



以下、phpのサンプル

<?php

//----------
//メール作成
//----------

//PHPMailer ライブラリ読み込み
use PHPMailer\PHPMailer\PHPMailer;//PHPMailerの呼び出し
require_once("./vendor/autoload.php" );//位置は適宜設定のこと

mb_language("japanese");//言語設定、内部エンコーディングを指定mb_internal_encoding("UTF-8");

$files = "generate.csv";//アドレスは適宜設定のこと
//日本語添付メールを送るための設定
$to = "hoge@hoge.jp"; //宛先 必須
//$cc = "";
//$Bcc = "";
$subject = "メールのタイトル名";
$body ="本文\n";
$body.="添付のcsvファイルをご確認のこと。\n";
$body.="\n";
$from = "hoge2@hoge.com"; //送り主
$fromname = "送信者名";

//PHPMailerのインスタンス生成
$mail = new PHPMailer(true);//Exceptionを使う場合はtrueにする事使わないときは()でも動く
$mail->CharSet = "iso-2022-jp";
$mail->Encoding = "7bit";

//SMTPサーバ設定
$mail->IsSMTP();
//$mail->SMTPDebug = 0; // 0:デバッグOFF 1:デバッグON
$mail->SMTPAuth = TRUE;
$mail->Host = 'mail.hoge.jp:587';
$mail->Username = 'hoge';
$mail->Password = 'hogehoge';
$mail->SMTPSecure = 'tls'; //TLS
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->AddAddress($to);
//$mail->AddCC($cc);
//$mail->AddBcc($Bcc);
$mail->From = $from;
$mail->FromName = mb_encode_mimeheader(mb_convert_encoding($fromname,"JIS","UTF-8"));
$mail->Subject = mb_encode_mimeheader(mb_convert_encoding($subject,"JIS","UTF-8"));
$mail->Body = mb_convert_encoding($body,"JIS","UTF-8");

//添付ファイル
$mail->AddAttachment($files);

//メールを送信
if (!$mail->Send()){
echo("Failed to send mail. Error:".$mail->ErrorInfo);
}else{
echo("Send mail OK.");
}


?>


このような記述で動作した。


後日追記
証明書を取得してpostfixのsmtpd_tls_cert_file などにPKCS12形式のcrtファイル
を配置し、テストしたところ、
verify_peerについての記述をしなくとも、正しく動いた。





twilio Enable Two-Factor Authentication (2段階認証)設定

Twilioから
[Action Required] You must enable Two-factor authentication on your Twilio accounts by October 12, 2020

というメールが来た。
中を見ると、2020年の10月12日からセキュリティ上2段階認証を要求します。
2段階認証を許容していないとロックアウトされてしまうので、設定変更してね
という内容だった。


    1.Login to Twilio Console
    2.Go to “User Settings” page (below your name on the top right corner) in Console
    3.In the "Enable Two-Factor Authentication (2FA)" section, select desired 2FA level
     and click “Update 2FA Settings” to save your selection.
    4.In the "Two-Factor Authentication (2FA) Methods" section, select the desired 2FA method
     and click “Update 2FA Method” to save your selection.
    5.Users enabling 2FA for the first time will be required to go through a one-time login
     and verification process with your new 2FA method of choice.

      手順に従って設定してみた。

      1.今までのようにログインする。
      2.右上のユーザー名をクリックして、User Settings画面に移動
      3.Enable Two-Factor Authentication (2FA) の
        30日毎に認証を選択し(毎回は流石に面倒と判断)
        Update 2FA Settings をクリック
      4.認証画面に飛ばされるので、 日本(+81)を選んで、ユーザ登録した際の
        sms番号(090-xxxx-yyyyなら90xxxxyyyy)を入力して送信
      5.携帯にsmsが飛んでくるので、認証コードを入力する。
      6.トラブル用の回復コードが飛んでくるので、携帯のsimが使えなくなった場合など
        の為に、メモっておいたほうがいいかも。
      7.無事ログインできた。

       メールにある、 In the "Two-Factor Authentication (2FA) Methods については、
      再度ログイン後に選択可能だったので、最初の一回目は テキストメッセージが
      飛んでくるようだ。
      認証コードを送信するための画面の説明は図入りの解説ページを設けても
      良いと思ったけど、twilioを使っている人は、全くの素人ではないだろうから
      自力で何とかするんだろう・・・。

      認証方法については、
      Authy app とあるので、アプリからの認証も出来る模様。
      https://apps.apple.com/jp/app/twilio-authy/id494168017 
      https://play.google.com/store/apps/details?id=com.authy.authy&hl=ja
      面白そうだけど、そもそもそんなにログインしないからテキストでいいや。

      2020/07/07

      inotifywait ディレクトリ監視から自動動作のサンプル

      ディレクトリ監視をして、業務効率化を図るべく、
      今更感満載ながらinotifywaitに手を出してみた。
      
      
      yumでインストールをする
      
      
      epelレポジトリにあるので、未導入時はepel-releaseをインストール
      # yum install epel-release 
       
      inotify-toolsをインストールする。
      # yum install inotify-tools 
      ヴァージョン確認 
      # inotifywait --help
      inotifywait 3.14
       
       
      動作は、ワンライナーでもOKだが、
      シェルスクリプトにて動作させたほうが、汎用性が高い。
       
      シェルスクリプトの例
      先駆者の方々の歴史を参考に
      今回は、pdftkと連携させ、監視ディレクトリにpdfファイルが新規作成されたら
      作業用ディレクトリに移動しPDFに作業済みスタンプを押して、
      作業済ディレクトリに保存という作業を想定。
       
      
      
      doSetStamp.sh
      
      
      ここから
      
      
      #!/bin/sh
      export PATH="/usr/local/bin:$PATH"#pdftkのパスを通す
      MONITORDIR="/home/hoge/work" #監視ディレクトリ指定
      ##スタンプは /home/hoge/stamp/back.pdfを想定
       
      inotifywait -m -r -e create --format '%w%f' "${MONITORDIR}" | while read NEWFILE
      do
        cd /home/hoge/work
        file=$(basename "${NEWFILE}" .pdf)
        mv "$file".pdf /home/hoge/work/"$file".pdf;
        cd /home/hoge/work/
        pdftk /home/hoge/work/"$file".pdf stamp ./stamp/back.pdf output /home/hoge/work/done/"$file"`date "+%Y%m%d"`.pdf;
        rm "$file".pdf
      done
      
      
      ここまで
      
      
      
      
      inotifywaitでなく、シェルのルールの問題だが、
      ダブルクオーテーションで括らないと、半角スペースでコマンドが止まってしまう。
      Windowsユーザが使うので、どうしても半角スペース問題は、加味しておかないと
      想定通りに動かない。
       
      バックエンドで動かす場合は、 
      $nohup doSetStamp.sh &
      のようにする。
      
      
      pdf以外のファイルをディレクトリに配置しない方法として、
      
      
      do~doneの制御にifで判別させて仕分ける方法が最も簡単だと思う。
      if [[ "$file" =~ .*pdf$ ]]; then  
       ここに動作させたい処理を書く; 
      else
       pdfファイル以外の処理を書く;  
      fi
       
       
      
      

      2020/05/14

      SPAM対策

      不覚にも、メールサーバのユーザパスワードが突破され、

      50件程度の踏み台にされてしまったので、対策を行った。

      先ずは、ユーザのパスワードを即座に変更

      # passwd <ユーザ名>
      Changing password for user <ユーザ名>.
      New password:
      Retype new password:
      passwd: all authentication tokens updated successfully.

      メーラー等の設定を修正する。

      スパム送信元のIPアドレスを探る。
        # tail -10000 /var/log/maillog | grep "LOGIN"
      May 14 05:29:03 www postfix/smtpd[xxxx]: xxxxxxxxxxxx: client=unknown[141.XXX.XXX.XXX], sasl_method=LOGIN, sasl_username=ごめんなさい・・・確かに、ヨーロッパのサーバ(ここも踏み台?)に突破されています。

      postfixに拒絶するIPアドレスを指定する。
      # vi /etc/postfix/main.cf

      末尾に
      smtpd_client_restrictions=
      permit_mynetworks,
      check_client_access hash:/etc/postfix/reject_ip,
      permit

      smtpd_client_restrictions で接続の許可、拒否の定義をする。

      permit_mynetworks, $mynetworksに書いたアドレスは許可する。

      check_client_access hash:/etc/postfix/reject_ip, 指定するipを拒否

      permit 上記以外を許可

      拒否するIPアドレス一覧を作成

      記載方法は、拒否したいIPアドレス、ドメイン + REJECTと記載。


      # nano /etc/postfix/reject_ip

      send.hogehoge.com REJECT
      141.XXX.XXX.XXX REJECT
      141.XXX.XXX REJECT

      reject_ipをDB形式に変換
      # postmap /etc/postfix/reject_ip

      Postfixをリロード
      # systemctl restart postfix

      設定が反映される。

      パスワードは、乱数生成が必須とまではいかないまでも、
      ある程度読まれないものにしないとだめだなぁ・・・
      突破されるとは思わなんだ・・・。

      2020/04/05

      slackの登録とbot起動 終了

      今更ながら、slackbotに手を出してみる

      参考にさせて頂いたところ
      https://se.miyabikno-jobs.com/entrance-labotlatori/

      Hubotを使う方法が簡単なようなので、上記を参考に導入

      01.slackのアカウント取得
      02.Hubotのアプリを追加
      03.HubotのAPIトークンを取得
      04.pythonのライブラリ slackbotをインストール
         $ sudo apt-get install python-3-pip
         $ sudo pip3 install slackbot
         $ mkdir slackbot
         $ cd slackbot
      05. $ touch slackbot_settings.py bot.py botmodule.py
         ・slackbot_settings.py:Botの設定を書くためのファイル(名称変更不可)
         ・bot.py:Botを起動するための実行ファイル(名前は任意)
         ・botmodule.py:Botの応答を記述するためのモジュール(名前は任意)
      06.$ nano slackbot_settings.py

      # 「API_TOKEN」にはSlackから取得したAPIトークンを記述
      API_TOKEN = "XXXXXXXXXXXXXXXXXXX"
      # 知らない言葉を聞いた時のデフォルトの応答
      DEFAULT_REPLY = "その言葉の意味は学習していません"
      # 外部ファイルを読み込む。botmodule.pyを読込
      PLUGINS = [
      'slackbot.plugins',
      'botmodule',
      ]

      07.$ nano bot.py

      from slackbot.bot import Bot
      # Botを起動する
      def main():
      bot = Bot()
      bot.run()
      if __name__ == "__main__":
      main()

      08.$ nano botmodule.py

      from slackbot.bot import respond_to
      from slackbot.bot import listen_to
      # 「respond_to」はメンションする(@でターゲットを指定すること)と応答する
      @respond_to('よろしく')
      def greeting_1(message):
      # Slackに応答を返す
      message.reply('よろしくお願いします!')
      # 「listen_to」はメンションがなくても応答する
      @listen_to('コニチハ')
      def greeting_2(message):
      message.reply('コニチハ')

      09.$ nohup python3 bot.py &
        ssh接続を切っても稼働するようにnohup ~ &で起動
            再度sshで接続し、プロセスを停止さえたい場合は、
            $ ps x
            にて pyhon3 bot.py のプロセスidを調べて、
        $ kill -9 
           として停止させる。

      ログインシェルがsshを閉じる際にSIGHUPを投げるため、プロセスが終了してしまうと思っていた。
      nohupはSIGHUPのシグナルハンドラをSIG_IGNに設定、つまり「受信しても無視」する設定にして、コマンドラインで指定されたジョブを実行している。 
      nohupで起動した時点ではRUNNNINGだが、端末が閉じられた後、標準入力に対してread()するようなプログラムでは、(5分ごとに読みなおしなどの動作が入るもの)はcloseしてしまっていて、
      EOFを返してきてしまう。


      nohupを使わなくても停止しないケースもあるようだが、説明が難しい。
      非常に詳しくテストされた解説が掲載されている
      https://www.glamenv-septzen.net/view/854
      実際は、SIGHUPのデフォルト処理はプロセス終了となっており、SIGHUPの受信にかかわらず、プロセスが終了するとき、そのプロセスがセッションリーダーだった場合、カーネルからそのセッションのフォアグラウンドプロセスグループに対してSIGHUPが送信されるらしい。
      また、カーネル側で放置しているバックグラウンドジョブに対しても、シェル(bash)側で終了時にSIGTERM/SIGCONTを送信する事があるためというのが実態のようだ。

      2019/12/12

      土日・祝祭日をカウントしないクエリ

      土日、祝祭日をカウントしないで、経過日数から処理を行いたい。

      休日テーブルのようなものを作って、LEFTJOINして加工
      のようなことをやっていたが、
      DBなくても動くものが欲しかったので、調べてみたオボエガキ。

      範囲を絞る方法として、
      次のように使う

      select 【id】 from 【table名】
      where (to_timestamp(to_char(now(),'YYYY-MM-DD'),'YYYY-MM-DD') -
      (to_timestamp(to_char(【table名】.【受付日】,'9999-99-99'),'YYYY-MM-DD'))) > interval '5 days'
      and
       (SELECT count(*) FROM generate_series(to_timestamp(to_char(【table名】.【受付日】,'9999-99-99'),'YYYY-MM-DD'),
      to_timestamp(to_char(now(),'YYYY-MM-DD'),'YYYY-MM-DD')-interval  '1 day',
      interval  '1 day') the_day
      WHERE (extract('ISODOW' FROM the_day) < 6
      and not(EXTRACT(MONTH FROM the_day) * 100
      + EXTRACT(DAY FROM the_day)
      IN (101, 113, 211, 223, 224, 320, 429, 503, 504, 505, 506, 723, 724, 810, 921, 922, 1103, 1123) ) ) ) >4

      idが 【table名】.【受付日】から5日以上経過(当日含まないので中4日)
      なお、条件として、【table名】.【受付日】がint2(8)の為、DATE型に変換している。

      解説
      副問い合わせをしているのだが、

      (SELECT count(*) FROM generate_series(to_timestamp(to_char(【table名】.【受付日】,'9999-99-99'),'YYYY-MM-DD'),
      to_timestamp(to_char(now(),'YYYY-MM-DD'),'YYYY-MM-DD')-interval  '1 day',
      interval  '1 day') the_day
      WHERE (extract('ISODOW' FROM the_day) < 6
      and not(EXTRACT(MONTH FROM the_day) * 100
      + EXTRACT(DAY FROM the_day)
      IN (101, 113, 211, 223, 224, 320, 429, 503, 504, 505, 506, 723, 724, 810, 921, 922, 1103, 1123) ) ) ) >4

       generate_series という集合を返す関数を使って、カウントが中4日となる様に制御している。

       generate_series
      (
      to_timestamp(to_char(【table名】.【受付日】,'9999-99-99'),'YYYY-MM-DD'),
      to_timestamp(to_char(now(),'YYYY-MM-DD'),'YYYY-MM-DD')-interval  '1 day',
      interval  '1 day'
      ) the_day

      始まり,終わり,間隔 と記載している。 
      終わりの中で -interval  '1 day' としているのは、当日を入れたくないため。

      WHERE
      (
      extract('ISODOW' FROM the_day) < 6
      and not
      (
      EXTRACT(MONTH FROM the_day) * 100
      +
      EXTRACT(DAY FROM the_day)
      IN
       (101, 113, 211, 223, 224, 320, 429, 503, 504, 505, 506, 723, 724, 810, 921, 922, 1103, 1123)
      )

      extract('ISODOW' FROM the_day) < 6 ISODOWは6が土曜、7が日曜となる。
      (DOWだと0が日曜、6が土曜)

      and not 以下の括りで、国民の休日をカウント対象から除外させている。

      EXTRACT(MONTH FROM the_day) * 100
      +
      EXTRACT(DAY FROM the_day)
      で月を100倍し、日と足す。
      これを 副問い合わせのIN関数
      (左側の式の結果が右側の式のどれかと等しい場合、結果は「真」を返す)
      を用いて、その日付だった場合は、2重否定となり、カウントされない。


      固定日以外の春分の日なんかは、国立天文台の算定式で括ってあげればいいんだが、
      このところ、天皇即位やオリンピックで変則が多いので、調べて直接日付を書いたほうが
      よさそう。

      春分の計算
      (EXTRACT(MONTH FROM date) = 3
      AND EXTRACT(DAY FROM date) = FLOOR(20.8431
      + 0.242194 * (EXTRACT(YEAR FROM date) - 1980))
      - FLOOR((EXTRACT(YEAR FROM date) - 1980) / 4))



      and not でそれぞれ括れば、除外できるが

      and not が直ぐに思いつかなかった・・・。

      2019/11/22

      PHP SMS APIサービスのテスト Twilio送信内容の確認

      昨日の投稿の続き

      送った内容をコンソールでなく、ブラウザ上で確認したい。
      検索等の実装も可能なようだ。
      先ずは、一覧を表示できないと話にならないので、サンプルを作成してみる。

      PHPサンプル list_messages.php

      <?php

      require("/autoloadが置いてあるでぃてくとりをフルパスで指定/autoload.php" );
      use Twilio\Rest\Client;

      //定義
      $sid    = "アカウントのSID";
      $token  = "アカウントのAUTHTOKEN";
      $twilio = new Client($sid, $token);

      $messages = $twilio->messages
                         ->read(array(), 20);//取り合えず20個まで

      foreach ($messages as $record) {
          print($record->to);//toで送信先の番号
          print($record->body);//送信メッセージの内容
      }

      ?>

      送信結果を取りたいのであれば、

      body
      date_sent
      status
      to
      body

      で十分な気がする。
      取得できる配列は以下のようだ。
      JSON APIの記載があった。


      "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      "api_version": "2010-04-01",
      "body": "testing",
      "date_created": "Fri, 24 May 2019 17:18:27 +0000",
      "date_sent": "Fri, 24 May 2019 17:18:28 +0000",
      "date_updated": "Fri, 24 May 2019 17:18:28 +0000",
      "direction": "outbound-api",
      "error_code": 30007,
      "error_message": "Carrier violation",
      "from": "+12019235161",
      "messaging_service_sid": "MGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      "num_media": "0",
      "num_segments": "1",
      "price": "-0.00750",
      "price_unit": "USD",
      "sid": "MMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      "status": "sent",
      "subresource_uris": {
      "media": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/SMb7c0a2ce80504485a6f653a7110836f5/Media.json",
      "feedback": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/SMb7c0a2ce80504485a6f653a7110836f5/Feedback.json"
      },
      "to": "+18182008801",
      "uri": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/SMb7c0a2ce80504485a6f653a7110836f5.json"

      2019/11/21

      PHP SMS APIサービスのテスト Twilio 登録から送信テスト完了まで

      SMS APIサービスの実装テストにあたりTwilioさんのトライアル版を試してみた。

      アカウント取得
      ブラウザがchromeでないと上手くいかなかった。
      https://jp.twilio.com/try-twilio/kddi-web
      2019年に業務提携があったようで、トライアルアカウント取得のページが
      複数存在するようだ。ここで、取得できた。

      性、名、メールアドレス、パスワード(14文字以上)
      を入力してトライアルアカウント作成をすると
      ログインする。
      その後、2段階式の認証が入り、SMSが有効な電話番号を求めてくるので、
      入力して送信すると、認証数列がSMSに届くので、
      それを入力して登録完了。

      PHPへの実装

      composerにてtwilio-phpのライブラリを取得

      任意のフォルダにて
      $ composer require twilio/sdk
      処理されて
       Generating autoload files

      サンプルプログラム sms_send.php

      <?php

      require("/autoloadが置いてあるでぃてくとりをフルパスで指定/autoload.php" );
      use Twilio\Rest\Client;

      $account_sid = 'ダッシュボードのアカウントSIDを記載';
      $auth_token = 'ダッシュボードのAUTH TOKENを記載';
      $twilio_number = '+120********';//取得したTwilioの電話番号テストは+120のみ許容

      $client = new Client($account_sid, $auth_token);
      $client->messages->create(
          '+8190********',//090の場合日本は+81なので090が+8190となる。トライアル時は登録した電話番号のみ許容される。
          [
              'from' => $twilio_number,
              'body' => 'SMS送信テスト'
          ]
      );

       ?>

      実行してみる
      $ php sms_send.php

      PHP Fatal error:  Uncaught exception 'Twilio\Exceptions\RestException' with message '[HTTP 426] Unable to create record: Upgrade Required' in /var/www/html/vendor/twilio/sdk/Twilio/Version.php:85
      Stack trace:
      #0 /var/www/html/vendor/twilio/sdk/Twilio/Version.php(219): Twilio\Version->exception(Object(Twilio\Http\Response), 'Unable to creat...')
      #1 /var/www/html/vendor/twilio/sdk/Twilio/Rest/Api/V2010/Account/MessageList.php(70): Twilio\Version->create('POST', '/Accounts/AC5c4...', Array, Array)
      #2 /var/www/html/maildistributer/test/twilio_test.php(20): Twilio\Rest\Api\V2010\Account\MessageList->create('+8190********', Array)
      #3 {main}
        thrown in /var/www/html/vendor/twilio/sdk/Twilio/Version.php on line 85

      ん?

      426エラーが返ってきたので、
      twilioの仕様を確認したところ、tls1.2を強要とのこと
       
      phpinfoで知らべたところ稼働中のサーバは
      opensslは1.2対応していたが、
      cURLのNSSが/3.15でTLS1.2に対応していなかった。(3.283.19.1-6以降以上を要求らしい)

      手っとり早い方法を探したところ、NSS/3.28以上のパッケージをインストールしてから
      curlをアップデートするのが最速とのことだったので、

      # yum install -y https://rpms.southbridge.ru/rhel7/stable/x86_64/southbridge-stable-release-1.0-el7.southbridge.noarch.rpm
       
      # yum update curl
      依存処理に結構時間がかかったが、何とか処理完了

      # systemctl restart httpd.service
      phpを再起動
       これも、壊したか?と不安になる位 えらく時間がかかった・・・。

      再度テスト
      $ php sms_send.php

      届いた!

      登録から送信完了まで作業時間が約1時間半だった。

      2018-9月にTLS1.3がリリースされたので、
      近いうちにTLS1.3(openssl 1.1.1)にアップデートが必要になるものと
      推測される。
      なにやら攻撃を受ける可能性があるなどの情報もありもう少し様子見しようかな
      と思っているが、そろそろ、PHPも7.2に引き上げが必要だろう。
      動作確認とか大変だなぁ

      2019/11/14

      CSPRでの乱数生成 (暗号論的疑似乱数生成関数)

      この度、セキュリティポリシーの高い
      パスワードの自動生成が必要になり、
      疑似乱数生成(PR)によりパスワードの生成をしようとしたころ、
      安全な乱数でないと駄目です。とNGをもらった。
      安全な乱数なんて量子コンピュータでもないと出来ないよ~~
      とか、
      エントロピーの無駄使いだ!!
      とか言っても解決しないので、簡単な方向を模索してみる。

      稼働サーバのPHPが5.4なので、当初randメソッドやmt_randメソッドを
      SHA256で包括して、SEEDをハッシュ値で生成するから
      疑似じゃないでしょ?で行こう思っていたが、
      駄目っぽい雰囲気だったので、もう少し調べてみたところ、
      PHP5.4でもライブラリを実装するとPHP7からの
      CSPRNGである「random_int」メソッドが使えたので、オボエガキ。

      ライブラリは、いつものGitHub様から取得
      公式からもリンクされるという素晴らしさ!!
      https://github.com/paragonie/random_compat

      適宜配置

      実際のソース
      <?php
      //ライブラリ読み込み
      require_once "./php_lib/random.php";
      // 利用可能な文字列の指定
      $chars = 'ABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789';
      // 変数初期化
      $passwd = '';
      // ランダムに文字列を生成(12文字)
      for ($i = 0; $i < 12; $i++) {
        $passwd .= $chars[random_int(0, 35)];//$charsの文字数を修正時は、必ず揃え、35を修正する。
        }
      //無限ループ注意!! 数字のみ英大文字のみを許容しない
        while ( (preg_match('/^[0-9]+$/', $passwd) || preg_match('/^[A-Z]+$/', $passwd)) ){
            $passwd = '';//一旦 $passwd をまっさらにする
          for ($i = 0; $i < 12; $i++) {
           $passwd .= $chars[random_int(0, 35)];//条件充足までループ
            }
        }
      //文字列表示
      echo $passwd;
      ?>

      こんな感じ

      やっぱり、困ったら一人で頑張らないで、公式サイトのメソッドを見るべきだ
      と感じた。

      2019/08/18

      apache SELinuxがEnforcingの時、特定のフォルダで読み書きを可能にしたい。

      安否確認を自主作成している際、PHPでファイルを読み書きできない状況になったので
      調べたところ、コンテキストをいじるには
      policycoreutils-python がインストールされていることが条件

      yum list policycoreutils-python で調べてなければ、インストールのこと


      /var/www/html/test 配下を許容するには
      # semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/test/(/.*)?"
      # restorecon -R /var/www/html/test/

       でSELinuxの例外を追加する必要がある。


      ls -alFZ /var/www/html/test
      で確認できる。


      嵌ったパターン
      既にファイルを生成していて、ディレクトリの属性を
      httpd_sys_rw_content_tに変えてrestoreconをしても、
      既にあるファイルの属性は変わらない 仕様である。
      これが分からず、さんざん悩んだ挙句、
      ls -alFZ /var/www/html/対象がいるディレクトリ/
      で調べたところ、案の定 rwの属性になっていなかった・・・。
      個別に属性付与か、いったん削除して再度scp等でアップするなりが必要。

      2019/07/01

      シェルスクリプトが動かない場合

      プログラムを書いて試験して
      予定通りに動いたので、cronで自動化をしようと
      シェルスクリプトを記載したが、
      うまく動かなかった。

      logを取ると、プログラムが無いといわれる。

      症状を調べると、改行がLFでなくCRLFになっていた。

      最近エディタにatomを使っているのだが、
      Windowsでatomを使うとデフォルトではCRLFを改行コードに
      しているとのことだった。

      atomの設定で、
      Core Packages ⇒ line-ending-selector に
      LF CRLF OS Default があるので、これを
      LFにしてあげれば良い。

      30分くらい嵌ってしまった。

      2019/06/17

      PDF解像度変更と一部のページ除去 gsコマンド(GhostScript)

      表題の操作をスキャン後のファイルに自動処理で行いたい。

      不要なページの削除方法
      ◆pdftkコマンドを使う

      今回はスキャナの為の台紙である一枚目を取り除きたいので、
      $ pdftk origin.pdf cat 2-end output modify.pdf
      となる。

      2枚目だけを取り除きたい場合は、
      $ pdftk origin.pdf cat 1 3-end output modify.pdf
      オプションの詳細


      解像度の変更
      ◆gsコマンドを使う

      オプションが非常に長いが、中途半端に記載すると
      処理に時間がかかったので、ファイルサイズ軽減に係るところは全部表記した。

      $ gs -q -sOutputFile="/home/user0463/out.pdf" -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -dSAFER -dQUIET -dCompatibilityLevel=1.4 -sColorConversionStrategy=GRAY -dProcessColorModel=/DeviceGray -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=72 -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=72 -dColorImageDownsampleType=/Bicubic -dColorImageResolution=72 -dEmbedAllFonts=false -dSubsetFonts=false -dPDFSETTINGS=/printer /home/user0463/moto.pdf

      解説
      -q 実行時にメッセージを出力しない。
      -sOutputFile="出力先ファイル記述"
      -sDEVICE=pdfwrite pdf以外の設定  gs -hで確認できる。
      -dNOPAUSE ページごとに処理に一時停止をさせない。(これを書かないと遅くなる)
      -dBATCH 一連処理させる。
      -dSAFER deletefileとrenamefileオペレータを無効
      -dQUIET 標準出力への情報を表示しない。
      -dCompatibilityLevel=1.4 1.3~1.6まで定義可
      -sColorConversionStrategy=GRAY GRAY,CMYK,RGBを指定
      -dProcessColorModel=/DeviceGray  DeviceGray,DeviceCMYK,DeviceRGBを指定
      -dGrayImageDownsampleType=/Bicubic /Subsample, /Average, /Bicubicを指定
      -dGrayImageResolution=72 解像度を指定 dpiを指定デフォルト72
      -dMonoImageDownsampleType=/Bicubic /Subsample, /Average, /Bicubicを指定
      -dMonoImageResolution=72 解像度を指定 dpiを指定デフォルト72
      -dColorImageDownsampleType=/Bicubic /Subsample, /Average, /Bicubicを指定
      -dColorImageResolution=72 解像度を指定 dpiを指定デフォルト72
      -dEmbedAllFonts=false フォントを埋め込むか? true,falseを指定
      -dSubsetFonts=false 標準フォントを埋め込むか? true,falseを指定
      -dPDFSETTINGS=/printer  /screen, /ebook, /printer, /prepress, /defaultを指定 



      PDFSETTINGS は 低解像度← screen,ebook,printer,prepress →高解像度
      defaultは元データを参照するようである。


      あとは、シェルスクリプトに記載して、cron や inotifywait
      で 自動処理させれば良い。
      gsコマンドは端折って記載するより全部書いたほうが早いと云うのが面白い。

      2019/05/17

      raspberrypi3のカメラを使ってメール応答型のスクリプト node.jsを活用 

      やりたいこと
      メールを受信すると、raspberrypi3のカメラで撮影し、
      メールに添付して指定のメールアドレスに送信するスクリプト。
      子供や愛犬の見守りなど目的は同じになるが、
      玄関の施錠をしたか?を最寄りの駅で気になった場合に
      万が一の為に戻りたくないので作成。
      玄関が開き戸だったら、もっといろいろな仕掛けができたのだが・・・。


       補足
       メール部分は自分でサーバ建てるのが大変なので、
       Gmailを利用し、node.jsを使ってメールの送信を常に監視し、
       監視中にメールが来るとそれをトリガーにして
       カメラで撮影、メールを送信する。

       node.jsは『非同期スクリプト』であるため、
       fs等を使う場合は、同期させた処理を行う必要がある。
       ここで、嵌ってしまった・・・。
       カメラの処理に時間がかかり、ファイルが生成される前に
       メール送信のためのファイル加工が動作してしまい、
       エラーが返って来てしまうので、
       別々に動作確認が終わったら、promise 等で同期処理をさせる。
       
       前提
       ・raspberry-pi3のカメラモジュールを認識済み
       ・Gmailのアカウント取得、安全性の低いアプリの許可」を有効にしてある。
       ・nodeが動かせること

      成果物◆◆
      'use strict';
      var inbox = require('inbox');
      //var nodemailer = require('nodemailer');
      //var iconv = require('iconv');
      //var conv = new iconv.Iconv("UTF-8", "UTF-8");
      const simpleParser = require('mailparser').simpleParser;
      var client = inbox.createConnection(false, 'imap.gmail.com', {
          secureConnection: true
          ,auth: {
              user:'Gmailアカウント@gmail.com'
              ,pass:'Gmailパスワード'
          }
      });

      client.on('connect', function() {
          console.log('connected');
          client.openMailbox('INBOX', function(error) {
              if (error) throw error;
          });
      });

      client.connect();

      client.on("new", function(message) {
          console.log('日時:' + message.date);
          console.log('送信者:' + message.from.name + '-' + message.from.address);
          console.log('タイトル:' + message.title);
      var stream = client.createMessageStream(message.UID);
      simpleParser(stream)
          .then(mail=> {
              console.log('本文(HTMLテキスト):' + mail.textAsHtml);
          })
          .catch(err=> {
              console.log(err);
          });

      //node.jsはデフォルトで非同期処理を行うため、
      //asyncを用いてカメラがsnap.jpgを生成するのを待ち
      //Gmailの送信(snap.jpgの添付)、snap.jpgの削除を行っている
      //pi-cameraでは上書き保存ができないため、メール送信後に削除しておく。

      const async = require('async'); //同期処理のためasync呼び出す

      async.series([
        function(callback) {
          console.log('カメラの処理開始');
          //pi-camera の動作ここから
          const PiCamera = require('pi-camera');
          const myCamera = new PiCamera({
            mode: 'photo',
            output: `${ __dirname }/snap.jpg`,
            width: 640,
            height: 480,
            nopreview: true,
          });
          myCamera.snap()
            .then((result) => {
              // Your picture was captured
            })
            .catch((error) => {
               // Handle your error
            });
         //pi-camera の動作ここまで
          //安全をみて7秒ディレイ
          setTimeout(callback, 7000);
        }, function(callback) {
          console.log('メールの返信開始');
          //Gmailで送信ここから
          var nodemailer = require('nodemailer')
          var fs = require('fs');
          // メッセージ内容
          var message = {
            from    : '送信元@gmail.com',
            to      : '送信先@gmail.com',
            subject : 'Reply to request',
            text    : 'Reply to request,Picture of the Pi-Camera !',
            attachments : [{
            filename: 'snap.jpg',
            content: fs.readFileSync('snap.jpg') //カメラのデータ生成待ち
          }]
          };
          var smtpConfig = {
            host: 'smtp.gmail.com',
            port: 465,
            secure: true, // SSL
            auth: {
              user : 'Gmailアカウント@gmail.com',
              pass : 'Gmailパスワード'
            }
          };
          var transporter = nodemailer.createTransport(smtpConfig);
          transporter.sendMail(message, function(err, response) {
            console.log(err || response);
          });
          //ファイル削除
          try {
            fs.unlinkSync('snap.jpg');
            console.log('snap.jpgを削除しました。');
          } catch (error) {
            throw error;
          }
        //Gmailで送信ここまで
          setTimeout(callback, 1000);
        }, function(callback) {
          return console.log('処理終了');
        }
      ], function(err, results) {
        if (err) {
          return console.log('err[' + err + ']');
        }
      });

      }); // client.connect() を閉じる。



      コマンド自体はプロンプト状態なので停止はcrtl+Zにて行う。
      sshで操作する場合は、forever等で動かさないと、ターミナル閉じた瞬間に
      Killされるので注意が必要。

      動作部分解説

      ◆メール受信トリガー部分
        node.jsのパッケージインストール
        $ npm install inbox
        $ npm install mailparser
      まんま、@hideito2000様の掲載内容を使わせていただいた。
      //ソース
      client.on("new", function(message) {
      console.log('日時:' + message.date);
      console.log('送信者:' + message.from.name + '-' + message.from.address);
      console.log('タイトル:' + message.title);
      var stream = client.createMessageStream(message.UID);
      simpleParser(stream)
      .then(mail=> {
      console.log('本文(HTMLテキスト):' + mail.textAsHtml);
      })
      .catch(err=> {
      console.log(err);
      });

      //この部分にカメラ処理とメール送信を組み込む
      });
      //ソースここまで

      ◆pi-cameraのインストール
      $ npm install pi-camera
      
      
      作成者のページのサンプルをそのまま使う
      //ソース
      const PiCamera = require('pi-camera');
      const myCamera = new PiCamera({
        mode: 'photo',
        output: `${ __dirname }/snap.jpg`,
        width: 640,
        height: 480,
        nopreview: true,
      });
      myCamera.snap()
        .then((result) => {
          // Your picture was captured
        })
        .catch((error) => {
           // Handle your error
        });
      //ソースここまで
       
      ◆nodemailer でメールを送信させるので、インストール
      $ npm i -D nodemailer
      
      
      ソースは皆さまの内容を参照させていただいた。
      ありがとうございます。
       //ソース
      var mailer = require('nodemailer');
      mailer.SMTP = {
          host: 'smtp.gmail.com',
          port: 465,
          secure: true, // SSL
          auth: {
            user : 'Gmailのid@gmail.com,
            pass : 'Gmailのパスワード'
          }
      };
      fs.readFile("./snap.jpg", function (err, data) {
          mailer.send_mail({
              sender: '送信元@gmail.com',
              to: '送信先@gmail.com',
              subject: 'snapshot-Ent!',
              body: 'Snapshot of the Entrance !',
              attachments: [{'filename': 'snap.jpg', 'content': picture}]
          }), function(err, success) {
              if (err) {
                  // Handle error
              }
      
          }
      });
      //ソースここまで
      
      
       
      ◆ファイルの削除◆  
       try {
            fs.unlinkSync('snap.jpg');
            console.log('snap.jpgを削除しました。');
          } catch (error) {
            throw error;
          }
      
      
       fsは非同期だが、同じコマンドであれば記載の順に動くようだ。
      駄目な場合は、 async の回数を増やせば良い。
       
      ◆同期処理◆
      
      
      async.series([
        function(callback) {    
       console.log('処理開始');
      このcallback間にメソッドを記載
          setTimeout(callback, 1000);
       }, function(callback) { console.log('処理2開始');
          setTimeout(callback, 1000);
          });
      }, function(callback) {
          return console.log('処理終了');
        }
      ], 
       
      
      
      期待した動きができたので、満足。