Categories
Programs IT Support

List the hosts in .ssh /config file, then ssh directly

This is a script to help you to ssh when you set many hosts in ssh .config file. I made Windows Powershell and Linux bash version scripts.

How to use

command: sshfc [Part of the host name]

If you type only sshfc, all hostnames set in the config file will be displayed.

If you type “sys” with the first argument, hostnames including “sys” will be displayed.

You can connect the host with ssh by selecting a number which is shown next to the hostname.

The command name is sshfc with the head of “ssh from .config”.

An example of use as follows.

C:> sshfc sys
1) system
2) test_system
3) dev_system
Press the number of the host to ssh. Press [c] if you want to cancel.'
: 3
ssh dev_system
hogehoge@192.168.1.100's password:
....

Install Windows Powershell version

Make bin folder and Add the folder to your PATH. And download the script file from Github and put the file under the bin folder.

New-Item ($HOME + "\bin") -ItemType Directory
[System.Environment]::SetEnvironmentVariable("Path", $env:Path + ";%USERPROFILE%\bin", "User")

git clone https://github.com/y-toy/windows_sshfc.git
cd windows_sshfc
Move-Item ./sshfc.ps1 ($HOME + "\bin")

Note

Try the following command if you are unable to run the script. This will loosen the restrictions of the script execution.

Set-ExecutionPolicy Unrestricted -scope CurrentUser

Install Linux Bash version

Download the script file from Github and move the file under PATH folder.

git clone https://github.com/y-toy/linux_sshfc.git
chmod +x ./linux_sshfc/sshfc
mv ./linux_sshfc/sshfc /usr/local/bin/

Note

I have recently changed my Windows ssh client to “Windows terminal” from Microsoft. The “Windows terminal” itself is excellent, but it is difficult to use when I select the destination of the SSH connection from ssh config file. I made this tool to cover the problem.

Since the tool I made was unexpectedly useful, I made the bash version so that I can use the “sshfc” command after entering the server as well.
Now I don’t have to think “What’s the hostname I set in my config file?”.

Categories
Programs IT Support

PHP ヤマト配送状況取得 2021年サイト変更対応

2021年9月 クロネコヤマトの荷物問い合わせシステムのページレイアウトが変更になって、送り状番号から荷物の配送状況を取得していたバッチがエラーとなった。
APIを使用すればよいという話だが、ヤマトビジネスメンバーズは他部署で契約&契約した人は退職で詳細不明になっている。まあ、面倒なので、PHPで問い合わせページから荷物状況を取得するプログラムを書いた。

実際はベタ書きしているが、かっこ悪いので、公開に当たりクラス化。1回テスト確認はしたが、使っていないので、エラーがああるかも。PHP5.4だが、7でも8でも動くと思う。取得した状況には「▶」が付いているので不要であれば消して使ってください。
内部でサイト解析のためDOMDocument使っているので使える環境のこと。以下、使い方とクラス。

使い方

$truckNumbers = array(
'444486855064',
'4444-9099-9090',
'444490999101',
); // いくつでも可

$objYamato = new clsGetYamatoDeliverStatus();
$aryDelivStatus = $objYamato->getYamatoDeliverStatus($truckNumbers);
if ($aryDelivStatus === false){ /* エラー ヤマト追跡番号 状況取得失敗 */ }

var_dump($aryDelivStatus);
/*
結果は[
(‐無しの追跡番号),
(‐有りの追跡番号),
状況日,
配達状況,
問い合わせ時間]の配列
array(3) {
  [0] =>
  array(5) {
    [0] =>
    string(12) "444486855064"
    [1] =>
    string(14) "4444-8685-5064"
    [2] =>
    string(5) "10/20"
    [3] =>
    string(16) "▶ 配達完了"
    [4] =>
    string(19) "2021/10/12 09:59:10"
  }
  [1] =>
  array(5) {
    [0] =>
    string(12) "444490999090"
    [1] =>
    string(14) "4444-9099-9090"
    [2] =>
    string(5) "10/20"
    [3] =>
    string(16) "▶ 配達完了"
    [4] =>
    string(19) "2021/10/12 09:59:10"
  }
  [2] =>
  array(5) {
    [0] =>
    string(12) "444490999101"
    [1] =>
    string(14) "4444-9099-9101"
    [2] =>
    string(5) "10/20"
    [3] =>
    string(16) "▶ 配達完了"
    [4] =>
    string(19) "2021/10/12 09:59:10"
  }
}
*/

荷物状況取得 クラス

<?php

class clsGetYamatoDeliverStatus{

	// 配列$truckNumbersに指定された追跡番号の状況を全て取得する。$truckNumbersは‐有でも無しでも可。
	// 戻り値は次の配列の配列 ["123456781234"(‐無しの追跡番号),"1234-5678-1234"(‐有りの追跡番号),mm/dd 状況日,配達状況,Y/m/d H:i:s 問い合わせ時間]
	// 戻り値がfalseの場合、エラー(ヤマト追跡サイトにアクセス出来ない)
	public function getYamatoDeliverStatus($truckNumbers){
		// 10件ずつヤマトのサイトからトラッキング情報を取得する。
		$numnerCnt = count($truckNumbers);
		$getTruckNumbers = array();
		$aryAllStatusInfo = array();
		for ($i=0;$i < $numnerCnt;$i++){
			$getTruckNumbers[] = $truckNumbers[$i];
			if (count($getTruckNumbers) == 10){
				$aryRetStatus = $this->getYamatoDeliverStatusCore($getTruckNumbers);
				$aryTemp = array_merge($aryAllStatusInfo, $aryRetStatus);
				if ($aryTemp === false){ return false; }
				$aryAllStatusInfo = $aryTemp;
				$getTruckNumbers = array();
			}
		}

		// 残っている場合
		if (count($getTruckNumbers) > 0){
			$aryRetStatus = $this->getYamatoDeliverStatusCore($getTruckNumbers);
			$aryTemp = array_merge($aryAllStatusInfo, $aryRetStatus);
			if ($aryTemp === false){ return false; }
			$aryAllStatusInfo = $aryTemp;
		}

		return $aryAllStatusInfo;
	}

	// ヤマトの追跡サイトにアクセスし、10件づつ結果を取得する。
	// 引数の$truckNumbersは配列で10件まで。
	// 戻り値は次の配列の配列 ["123456781234"(‐無しの追跡番号),"1234-5678-1234"(‐有りの追跡番号),mm/dd 状況日,配達状況,Y/m/d H:i:s 問い合わせ時間]
	// 戻り値がfalseの場合エラー(サイトにアクセス出来ない場合のみ)
	private function getYamatoDeliverStatusCore($truckNumbers){
		$aryRet = array();

		$url = 'https://toi.kuronekoyamato.co.jp/cgi-bin/tneko';
		$tnumCnt = count($truckNumbers);

		$aryPOST = array(
			'mypagesession' => '',
			'backaddress' => '',
			'backrequest' => '',
			'number00' => 2
		);
		$j=0;
		for ($i=1;$i<=10;$i++){
			$postName = 'number';
			if ($i < 10){
				$postName .= '0' . $i;
			}else{
				$postName .= $i;
			}
			if ($j < $tnumCnt){
				$aryPOST[$postName] = $truckNumbers[$j];
			}else{
				$aryPOST[$postName] = '';
			}
			$j++;
		}

		$http_build = http_build_query($aryPOST,'','&');
		$header = array(
			"Content-Type: application/x-www-form-urlencoded",
			"Content-Length: ".strlen($http_build)
		);
		// オプション
		$opts = array("http" => array(
			"method" => "POST",
			"header"  => implode("\r\n", $header),
			"content" => $http_build)
		);
		$scc = stream_context_create($opts);

		// 読み取り
		$get_html = file_get_contents($url, false, $scc);
		if ($get_html === false){ return false; }

		// staus取得時間
		$getTime = Date('Y/m/d H:i:s');

		$dom = new DOMDocument();
		$ret = $dom->loadHTML($get_html);

		$divResult = $this->getElementsByClassName($dom, 'tracking-box-area', 'div');
		$divLen = count($divResult);
		for($i=0;$i < $divLen;$i++){
			// このdivの下のinputのvalueを取得
			$domInputs = $this->getDomNodeFromTagName($divResult[$i], 'input');
			if (count($domInputs) == 0){ continue; }
			$trNum = $domInputs[0]->attributes->getNamedItem('value')->nodeValue;
			if ($trNum == ''){ continue; }

			// このdiv以下の日付を取得
			$date = '';
			$dateNodes = $this->getDomNodeFromClassName($divResult[$i], 'data date');
			if (count($dateNodes) == 0){ continue; }
			$date = $dateNodes[0]->nodeValue;

			// このdiv以下の状況を取得
			$state = '';
			$dateStates = $this->getDomNodeFromClassName($divResult[$i], 'data state');
			if (count($dateStates) == 0){ continue; }
			$state = $dateStates[0]->nodeValue;

			$aryRet[] = array(
				str_replace('-','',$trNum), // ‐なし
				$trNum, // ‐あり
				$date, // 状況になった日付
				$state, // 状況
				$getTime // 取得時刻
			);

		}

		return $aryRet;
	}

	private function getElementsByClassName($dom, $ClassName, $tagName=null) {
		if($tagName){
			$Elements = $dom->getElementsByTagName($tagName);
		}else {
			$Elements = $dom->getElementsByTagName("*");
		}
		$Matched = array();
		for($i=0;$i<$Elements->length;$i++) {
			if($Elements->item($i)->attributes->getNamedItem('class')){
				if(strpos($Elements->item($i)->attributes->getNamedItem('class')->nodeValue, $ClassName) !== false) {
					$Matched[]=$Elements->item($i);
				}
			}
		}
		return $Matched;
	}

	// 子ノード以下に指定のタグ名を持つdomNodeの一覧を返す。
	private function getDomNodeFromTagName($domNode, $tagName){
		$aryRet = array();
		foreach ( $domNode->childNodes as $chiledNode ){
			if ($chiledNode->nodeName == $tagName){
				$aryRet[] = $chiledNode;
			}
			if ($chiledNode->hasChildNodes()){
				$aryTemp = $this->getDomNodeFromTagName($chiledNode, $tagName);
				foreach($aryTemp as $temp){
					$aryRet[] = $temp;
				}
			}
		}
		return $aryRet;
	}

	// 子ノード以下に指定のクラス名を持つdomNodeの一覧を返す。
	private function getDomNodeFromClassName($domNode, $className){
		$aryRet = array();
		foreach ( $domNode->childNodes as $chiledNode ){
			if ($chiledNode->hasAttributes()){
				$nodAttr = $chiledNode->attributes->getNamedItem('class');
				if ($nodAttr != null){
					$nodeClasses = $nodAttr->nodeValue;
					if (strpos($nodeClasses, $className) !== false){
						$aryRet[] = $chiledNode;
					}
				}
			}
			if ($chiledNode->hasChildNodes()){
				$aryTemp = $this->getDomNodeFromClassName($chiledNode, $className);
				foreach($aryTemp as $temp){
					$aryRet[] = $temp;
				}
			}
		}
		return $aryRet;
	}
}
Categories
Programs IT Support

cordova-plugin-buildinfoのiOSでDebugがfalseになる。

Cordovaのplugin cordova-plugin-buildinfoを利用した際に、iOSでdebugビルドしても”BuildInfo.debug”がfalseになって困っていたところ、以下の方法で解決できたので、備忘録として残しておく。

1)プラグインのバージョンを4.0以上に上げる。
関係ないが、4.0以下だと「このプラグインのせいでiOSのアプリが異常に遅くなることがある」との投稿があったので、とりあえず4.0以上に上げた。

2)XCODEでプロジェクトを開きBuild Settingsから以下2点を変更する。

・Apple LLVM 9.0 – Preprocessing -> Preprocessor Macrosの下にdebugという項目があるので、そこに”DEBUG=1″を追加する。(”Macros”で設定項目を検索するとすぐ見つかる)
・Swift compiler – Custom Flags -> Other Swift Flags
の下にdebugという項目があるので、”-D DEBUG”を追加する。
(”Swift”で設定項目を検索すると見つかる。)

※XCodeプロジェクトの開き方は、Cordova環境直下の./platforms/iosの下に拡張子”.xcworkspace”のファイルがあるのでこれを開く。同じようなファイルで拡張子”.xcodeproj”があるが、こちらではないので注意。

これで、デバッグビルドの場合のみ[BuildInfo.debug]がfalseとなった。

Categories
IT Support IT Support

qmail メール送信時エラー( TLS connect failed )までが長い

qmailでメール送信後、数日してから「メールが送信できなかった」とのエラーメールが返ってくるとの相談があった。

エラーメールの内容はだいたい以下のような感じ。

Hi. This is the qmail-send program at xxxxxxxx.
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.

XXXXXXX
TLS connect failed: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1
alert protocol version; connected to xxxxxxxxx
I'm not going to try again; this message has been in the queue too long.

以下のような場合もある。
LS connect failed: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3

どうも相手先のサーバに接続できなかった場合、延々とリトライをしているようだ。
設定を調べてみると、設定ファイル「/var/qmail/control/queuelifetime」に秒数を設定するとその時間以上はリトライをしなくなるらしい。デフォルト値は7日(604800)であった。送れなかったらさっさとエラーを返してほしいが、デフォルト値が長いのにあまり短くするのも怖いので、とりあえず1時間(3600)を設定した。

vi /var/qmail/control/queuelifetime
---
3600
---

ファイルには数字のみ記入する。再起動が必要なので以下をして終了。

qmailctl stop
qmailctl start

qmailctl restartはプロセスが残ってしまうので当方環境ではNGだった。

Categories
IT Support IT Support

PDFを印刷した際に一部のページでフォントがおかしくなる

「フォントがおかしい」1年に数回はこの厄介な質問を受けます。今回も、「PDFを印刷したら、XXページ目のフォントだけ違う、何とかしてくれ」という問い合わせが来ました。

該当のPDFを表示した段階では全く違いがわからなかったものの、印刷後の紙で確認すると確かに一部のページで若干文字が濃くなっている感じがします。

PDF自体はシステムから出力していて、特定のページでフォントを変えるような処理はしていませんし、そのシステムを組んでから数年、そんな質問が来たことはありませんでした。

「印刷機の可能性が高く、どうにもなりませんよ。」と回答しかけたところで、思いあたりました。

「PDFを表示するソフトのせいか」

試してみると、Windows 10の旧EdgeでPDFを表示し印刷した時だけ特定のページでフォントがほんの少し濃くなります。Adobe ReaderやChromeで表示、印刷したときは大丈夫なので、旧Edgeが原因なのは間違いなさそうです。

なので「面倒だけど、Edgeで表示したPDFをダウンロードして、それからChromeで表示して印刷してください。」と回答して終わりです。

まあ、面倒だからEdgeで印刷するのでしょうけど。。

Categories
OS IT Support

FreeBSD : sshやtelnetでログインしたまま、ターミナルを閉じてしまった場合の対処方法

掲題ままですが、残った仮想端末のプロセスを削除する方法を書いておきます。通常、ターミナルを落としたら、プロセスも消えるのでこのような状況にはならないのですが、不安なときの確認にもなるので。。。

  1. ターミナルから切断したユーザでログインし、以下のコマンドを打ち、現在の仮想端末の番号を確認します。
    > who
    以下のような結果が出ます。
    taro  pts/0 6月 16 04:52 (xxxxxx.or.jp)
    taro  pts/1 6月 16 04:56 (xxxxxx.or.jp)
    最も新しくログインしたものが自分なので、ここではpts/1が現在のターミナルです。
     
  2. そのまま以下のコマンドを実行し、仮想端末のプロセスを探します。
    > ps -aux | grep `whoami`
  3. 以下のような結果が表示されます。
    root 27048 0.0 1.4 20428 7976 - Is 04:52 0:00.02 sshd: taro [priv] (sshd) 
    taro 27050 0.0 1.4 20432 8008 - S 04:52 0:00.02 sshd: taro@pts/0 (sshd) 
    root 27124 0.0 1.4 20428 8012 - Is 04:56 0:00.02 sshd: taro [priv] (sshd) 
    taro 27126 0.0 1.4 20432 8036 - I 04:56 0:00.00 sshd: taro@pts/1 (sshd) 
    taro 27051 0.0 0.5 12628 2880 0 Ss 04:52 0:00.01 -sh (sh) 
    taro 27132 0.0 0.4 12324 2636 0 R+ 04:57 0:00.00 ps -aux
    
  4. 古いプロセスをkillします。
    kill 27050