プログラミングとかLinuxとかの備忘録

プログラミング、Linuxでハマった箇所や環境構築のメモ

Linux用プロキシ設定いろいろ

プロキシ下でLinuxを使う際のメモ
を参考にした自分用のメモ

Fedora23 (Mate Desktop)を使用してプロキシ設定を行った

システム全体の設定

Fedora23では/etc/profile.d/が存在したため/etc/profile.d/proxy.shを作成し,実行権限を与えた

$ sudo vi /etc/profile.d/proxy.sh
HOST="ホスト名orホストIP"
PORT="ポート"
PROXY=${HOST}:${PORT}

export http_proxy=http://${PROXY}
export https_proxy=https://${PROXY}
export ftp_proxy=ftp://${PROXY}

export HTTP_PROXY=${http_proxy}
export HTTPS_PROXY=${https_proxy}
export FTP_PROXY=${ftp_proxy}

local_ip=$(echo 192.168.1.{1..255})
local_ip=${local_ip// /,}
export no_proxy=127.0.0.1,localhost,${local_ip}

$ sudo chmod u+x /etc/profile.d/proxy.sh

しかし,これではFirefox等がネットにつながらなかったので,上記の設定はやめGUIから設定を行った

$ mate-network-properties

f:id:vild:20200606174127p:plain
Linux Proxy 1

dnf

最終行にプロキシ設定を追加する

$ sudo vi /etc/dnf/dnf.conf
[main]
gpgcheck=1
installonly_limit=3
clean_requirements_on_remove=true
proxy=http://ホスト名:ポート

随時更新予定


References

virtualenvでOpenCVを使う

virtualenv上のpipではopencvをインストールできなかったが
Is it possible to run opencv (python binding) from a virtualenv? - Stack Overflow

I found the solution was that I had to copy over cv2.so and cv.py to the directory running the virtualenv, then pip install numpy. To do this on Ubuntu 12.04 I used.

とあった.

仮想環境の作成

$ mkvirtualenv --python /opt/python2.7.11/bin/python2.7 --no-site-packages opencv

OpenCVのインストール

$ sudo dnf -y install opencv-python

システムのPythonを使用してインストールされたパスの確認を行う

$ python -c "import cv; print cv.__file__"
/usr/lib64/python2.7/site-packages/cv.pyc

$ ls /usr/lib64/python2.7/site-packages/ | grep cv
cv.py
cv.pyc
cv.pyo
cv2.so

これらのファイルへのシンボリックリンクを仮想環境内に作成する

$ cd ${WORKON_HOME}/opencv/lib/python2.7/site-packages
$ ln -s /usr/lib64/python2.7/site-packages/cv* ./

ここで,OpenCVで使用するnumpyをインストールする

$ workon opencv
(opencv)$ pip install numpy
Successfully installed numpy-1.10.4

動作確認

$ workon opencv
(opencv)$ python
>>> import cv
>>> import cv2

問題なくimportできた

PhpStormのインストールと初期設定

インストール環境

Fedora23にPhpStorm10.0.3をインストールした

$ cat /etc/redhat-release
Fedora release 23 (Twenty Three)

ダウンロード

PhpStorm IDE :: JetBrains PhpStorm
Download Nowからダウンロードする

または,

$ wget https://download.jetbrains.com/webide/PhpStorm-10.0.3.tar.gz

PHP実行環境のインストール

PHPインタプリタをインストール

$ sudo dnf -y install php-devel

$ php --version
PHP 5.6.18 (cli) (built: Feb  4 2016 12:03:13) 
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

次に,PHPのデバッガをインストール

$ sudo dnf -y install php-pecl-xdebug

インストール・初期設定

$ tar xf PhpStorm-10.0.3.tar.gz

$ cd PhpStorm-10.0.3/bin
$ ./phpstorm.sh

PHP Interpreterの設定

File -> Settingsから以下の設定を行う

ローカルのPHP実行環境の設定

-> Languages&Frameworks -> PHPを開き,
Interpreter:の右の...から

+を押しPHP Executable/usr/bin/phpにする.

f:id:vild:20200606172721p:plain
PhpStorm1

LiveEditの設定

編集中のファイルのブラウザプレビューを自動的に更新するLiveEditを
Live Editing of HTML, CSS, and JavaScript
に従いインストールする

Google Chromeのインストール

LiveEditはGoogle Chromeしか対応していないのでGoogle Chromeをインストールする必要がある

  1. Chrome ブラウザからダウンロードしインストールする
  2. 拡張機能: JetBrains IDE Supportをインストールする

LiveEditプラグインのインストール

PhpStormのヘルプ(Live Edit)には LiveEditはデフォルトでインストール&有効化されていると書いてあったが,
インストールされていなかったので以下の手順でインストールした

  1. Pluginsの検索ボックスでLiveEditと検索
  2. Install JetBrains plugin...をクリック
  3. Installをクリックする

f:id:vild:20200606172808p:plainf:id:vild:20200606172810p:plain
PhpStorm 2,3

再起動するとBuild, Execution, Deployment -> Debugger -> Live Editから設定できるようになる
(UpdateAuto in (ms)に変更すると,CSSの変更等も自動で反映するようになる)

以上の手順でセットアップした後にRun -> Debug (Ctrl + F9)で自動リロードモードに入れる

その他

  • 行番号を表示: Editor -> General Appearance -> Show line numbers
  • 空白を表示 : Editor -> General Appearance -> Show whitespaces

Linuxでのユーザ/グループ操作

ユーザー管理

ユーザ作成

$ sudo useradd -u ${UID} --create-home ${USER_NAME}

UIDの変更

$ sudo usermod -u ${NEW_UID} ${USER_NAME}

$ id
uid=${NEW_UID}(${USER_NAME}) ...(省略)

パスワード変更

$ sudo passwd ${USER_NAME}

または

$ su ${USER_NAME}
$ passwd

所属グループの管理

  • 所属グループを追加
$ sudo gpasswd -a ${USER_NAME} ${GROUP}
  • 所属グループを削除
$ sudo gpasswd -d ${USER_NAME} ${GROUP}

sudoできるようにする

ユーザを

グループに追加する

$ sudo gpasswd -a ${USER_NAME} wheel

グループの管理

グループ作成

GIDの変更

$ sudo groupmod -g ${NEW_GID} ${GROUP_NAME}

PT3サーバ自動起動・シャットダウン用スクリプト

BIOSの設定

RTCで起動できる様に設定する

Asrock H77M-ITXの場合は
Advanced Screen -> ACPI Configuration -> RTC Alarm Power On

自動復帰の確認

以下のコマンドを実行し,3分後に自動的に起動されることを確認する

$ echo `date +%s -d +3min` | sudo tee /sys/class/rtc/rtc0/wakealarm
$ sudo shutdown -h now

上記のコマンドで成功しない場合はrtcwakeを使用すると成功するかもしれません

スクリプト作成

とりあえず,スクリプト全体は以下の通り.

#!/bin/python
# -*- coding: utf-8 -*-

import subprocess
import datetime
import time

import logging
from logging.handlers import RotatingFileHandler

SUDO_PASS="実行ユーザのパスワード\n"


log_handler = RotatingFileHandler(
    filename="/var/www/html/epgrec/video/pt3_savepower.log",
    mode="a",
    maxBytes=1000000, backupCount=3,
    encoding="utf-8",
    delay=False
)
# log_handler.setLevel(logging.INFO)
log_handler.setFormatter(
    logging.Formatter("%(asctime)s - %(levelname)-8s - %(message)s")
)

logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(log_handler)


def is_terminal_login():
    stdout, stderr = subprocess.Popen(
        ["/usr/bin/who"],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    ).communicate()

    result = len([line for line in stdout.split("\n") if not line == ""])

    logger.info("[terminal]: %d" % result)
    return False if result is 0 else True


def is_recording_at():
    stdout, stderr = subprocess.Popen(
        ["sudo", "-S", "/bin/atq"],
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    ).communicate(SUDO_PASS)
    result = len([line for line in stdout.split("\n") if "=" in line])

    logger.info("[rec_at  ]: %d" % result)
    logger.debug(stdout)
    return False if result is 0 else True


def is_recording_ps():
    stdout, stderr = subprocess.Popen(
        ["/bin/ps", "-el"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    ).communicate()

    ps_recpt1 = [line for line in stdout.split("\n") if "recpt1" in line]
    result = len(ps_recpt1)

    logger.info("[rec_ps  ]: %d" % result)
    logger.debug(stdout)
    return False if result is 0 else True


def is_port_established():
    stdout, stderr = subprocess.Popen(
        ["sudo", "-S", "/sbin/lsof", "-i", ":ssh,http,https"],
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    ).communicate(SUDO_PASS)

    port_est = [line for line in stdout.split("\n") if "ESTABLISHED" in line]
    result = len(port_est)

    logger.info("[port    ]: %d" % result)
    logger.debug(stdout)
    return False if result is 0 else True


def convert_timestr(atq_line):
    result = atq_line.replace("Jan", "01")
    result = result.replace("Feb", "02")
    result = result.replace("Mar", "03")
    result = result.replace("Apr", "04")
    result = result.replace("May", "05")
    result = result.replace("Jun", "06")
    result = result.replace("Jul", "07")
    result = result.replace("Aug", "08")
    result = result.replace("Sep", "09")
    result = result.replace("Oct", "10")
    result = result.replace("Nov", "11")
    return result.replace("Dec", "12")


def get_at_schedules():
    stdout, stderr = subprocess.Popen(
        ["sudo", "-S", "/bin/atq"],
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    ).communicate(SUDO_PASS)

    schedule = []
    for line in stdout.split("\n"):
        if line is "":
            continue

        tmp = line.split()
        time_str = convert_timestr(" ".join([tmp[5], tmp[2], tmp[3], tmp[4]]))
        time_str = datetime.datetime.strptime(time_str, "%Y %m %d %H:%M:%S")
        unixtime = int(time.mktime(time_str.timetuple()))
        schedule.append(unixtime)

    return schedule


def get_closest_schedule():
    return sorted(get_at_schedules())[0]


def is_scheduled(unixtime):
    return any(s == unixtime for s in get_at_schedules())


def set_getepg_schedule(unixtime):
    if is_scheduled(unixtime):
        logger.info("getepg is already scheduled")
    else:
        getepg_at = datetime.datetime.fromtimestamp(unixtime).strftime("%y%m%d%H%M")
        stdout, stderr = subprocess.Popen(
            ["/bin/at", "-f", "/var/www/html/epgrec/video/at_getepg.sh", "-t", getepg_at],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        ).communicate()

    logger.info("[getepg  ]: %s" % datetime.datetime.fromtimestamp(unixtime))


def set_rtcwake(unixtime):
    logger.info("[wakeup  ]: %s" % datetime.datetime.fromtimestamp(unixtime))
    stdout, stderr = subprocess.Popen(
        ["sudo", "-S", "/usr/sbin/rtcwake", "-m", "off", "-t", str(unixtime)],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    ).communicate(SUDO_PASS)

    logger.debug(stdout)


def main():
    logger.info("=============== START LOGGING ===============")

    if any([
            is_terminal_login(),
            is_recording_at(),
            is_recording_ps(),
            is_port_established()
        ]):
        logger.info("someone using")
    else:
        now = int(time.time())
        closest_waketime = now + 1800
        closest_schedule = get_closest_schedule()
        wake_to_record = closest_schedule - 300
        wake_to_getepg = closest_schedule - 1800
        getepg_time = wake_to_getepg + 300

        logger.info("NOW  : %s" % datetime.datetime.fromtimestamp(now))
        logger.info("WEPG : %s" % datetime.datetime.fromtimestamp(wake_to_getepg))
        logger.info("GEPG : %s" % datetime.datetime.fromtimestamp(getepg_time))
        logger.info("WREC : %s" % datetime.datetime.fromtimestamp(wake_to_record))
        logger.info("CLOSE: %s" % datetime.datetime.fromtimestamp(closest_schedule))

        if wake_to_getepg > closest_waketime:
            set_getepg_schedule(getepg_time)
            set_rtcwake(wake_to_getepg)
        elif wake_to_record > closest_waketime:
            set_rtcwake(wake_to_record)
        else:
            logger.info("close to wakeup time")


if __name__ == '__main__':
    main()

グローバルスコープ

  • SUDO_PASS
    スクリプト実行ユーザーのパスワードを入れてsudoを実行できるようにする。
    "パスワード\n"のように末尾に\nが入っているが,これがパスワード入力後のエンターキーの代わりになる。
  • log_handlerに循環ログを定義する

main()

  • is_terminal_login()
    ターミナルにログインしているユーザがいるか
  • is_recording_at()
    録画中かをatコマンドで確認
  • is_recording_ps()
    録画中かをpsコマンドで確認
  • is_port_established()
    ssh,http,httpsのポートが使用中(接続中)であるかを確認

で,サーバの使用状況を確認し,シャットダウンして問題がなければ,

  • now = int(time.time())
    現在時刻
  • closest_waketime = now + 1800
    次回起動の最短時刻(シャットダウンした場合に現在時刻から1800秒以内に起動する必要がある場合はシャットダウンしないようにするための値)
  • closest_schedule = get_closest_schedule()
    一番近い録画開始時刻
  • wake_to_record = closest_schedule - 300
    録画のために起動する時間(起動から録画開始まで,300秒の余裕をもたせる)
  • wake_to_getepg = closest_schedule - 1800
    時間に余裕がある場合は録画開始の1800秒前に起動しEPGの更新を行う
  • getepg_time = wake_to_getepg + 300
    EPGの更新を行う場合にEPGの更新を開始する時刻(起動からEPG更新開始まで,300秒の余裕をもたせる)

上で設定した時刻を使用し

  • 時間に余裕がある(wake_to_getepg > closest_waketime)場合
    atEPG取得のスケジュールを登録した後,rtcwakeでシステムを停止する
  • EPG更新の余裕がない(wake_to_record > closest_waketime)場合は
    rtcwakeでシステムを停止するだけにする
  • それ以外の場合 現在時刻から30分以内に録画開始する必要があるのでPCへの負荷を考慮し,シャットダウンせずに待機する

is_terminal_login()

whoを使用し,ログイン中のユーザを取得する

以下のコマンドを実行し,行数を数えることでログイン中のユーザがいるかを判断する

$ who
ユーザー名    pts/0        2016-01-23 18:42 (192.168.19.5)
ユーザー名    pts/1        2016-01-23 19:09 (192.168.19.5)

whoの実行結果stdoutに対し,以下の処理をすることで行数を数える。

len([line for line in stdout.split("\n") if not line == ""])

whoの実行結果を\nで分割すると

[
    "ユーザ1...",
    "ユーザ2...",
    ""
]

のように最後の改行コード後にも文字列があるように分割されるので
if not line == ""を入れている

is_recording_at()

atqを使用し,実行中のatジョブを取得する

$ sudo atq
524 Fri Jan 29 00:29:00 2016 a ユーザー名
455 Sat Jan 23 02:30:00 2016 = ユーザー名
465 Sun Jan 24 03:45:00 2016 a ユーザー名
466 Sun Jan 24 12:15:00 2016 a ユーザー名
467 Sun Jan 24 09:35:00 2016 a ユーザー名

sudo atqの実行結果stdoutに対し,以下の処理で=を含む行数を数える。

len([line for line in stdout.split("\n") if "=" in line])

is_recording_ps()

psを使用し,recpt1を使用しているプロセスがあるかを確認する

$ ps -el
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
...
0 S  1000  2936  2935  0  82   2 - 68925 hrtime ?        00:00:00 recorder.php
0 S  1000  2940  2936  0  82   2 -  2902 wait   ?        00:00:00 do-record.sh
0 S  1000  2941  2940  4  82   2 - 59385 pt3_dm ?        00:02:01 recpt1
0 S     0  2942     1  0  80   0 - 61852 poll_s ?        00:00:00 pcscd
...

ps -elの実行結果stdoutに対し,以下の処理でrecpt1を含む行数を数える。

len([line for line in stdout.split("\n") if "recpt1" in line])

is_port_established()

lsofを使用し,ssh,http,httpsが使用されているかを確認する

$ sudo lsof -i :ssh,http,https
COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1081  root    3u  IPv4  17772      0t0  TCP *:ssh (LISTEN)
sshd    1081  root    4u  IPv6  17783      0t0  TCP *:ssh (LISTEN)
nginx   1127  root    6u  IPv4  18486      0t0  TCP *:http (LISTEN)
nginx   1128 nginx    3u  IPv4  27768      0t0  TCP PT3:http->192.168.19.5:34158 (ESTABLISHED)
nginx   1128 nginx    6u  IPv4  18486      0t0  TCP *:http (LISTEN)
sshd    3032  root    3u  IPv4  25384      0t0  TCP PT3:ssh->192.168.19.5:60890 (ESTABLISHED)
sshd    3034   pt3    3u  IPv4  25384      0t0  TCP PT3:ssh->192.168.19.5:60890 (ESTABLISHED)
sshd    3043  root    3u  IPv4  25436      0t0  TCP PT3:ssh->192.168.19.5:60902 (ESTABLISHED)
sshd    3045   pt3    3u  IPv4  25436      0t0  TCP PT3:ssh->192.168.19.5:60902 (ESTABLISHED)

sudo lsof -i :ssh,http,httpsの実行結果stdoutに対し,以下の処理でESTABLISHEDを含む行数を数える。

len([line for line in stdout.split("\n") if "ESTABLISHED" in line])

get_at_schedule()

atqでスケジュールされているスケジュールの各時刻をunixtimeに変換したリストを返す

atqからは以下のフォーマットで出力されるので

467  Sun Jan 24 09:35:00 2016 a ユーザー名

以下のようにフォーマットを変更し
convert_timestr()関数で月コード(Jan)を数値(01)に変換する

2016 01 24 09:35:00

この文字列を以下でunixtimeに変換する

time_str = datetime.datetime.strptime(time_str, "%Y %m %d %H:%M:%S")
unixtime = int(time.mktime(time_str.timetuple()))

get_closest_schedule()

get_at_schedule()から得られたunixtimeのリストをソートし,最小値(最も近いスケジュールのunixtime)を取得する

set_getepg_schedule()

引数のunixtimeの時刻にatEPG更新をスケジュールする

  • サーバが自動起動される前に手動でサーバを起動した場合には同じ時刻に複数EPG更新がスケジュールする可能性があるのでis_scheduled()で引数のunixtimeの時刻にatにスケジュールがないかを確認する
  • atは直接php(getepg.php)を実行できないため,以下のシェルスクリプトを経由してスケジュールを登録する
#!/bin/bash

/var/www/html/epgrec/getepg.php

set_rtcwake()

rtcwakeを使用し,自動起動時刻を設定する
また,rtcwakeを実行すると自動的にシャットダウンされる

スクリプトのcron実行

sudoを含むスクリプトをcronで実行すると

sudo: sorry, you must have a tty to run sudo

と言われ実行できないのでttyなしでsudoできるようにする

今回はPT3専用のサーバなので簡単に以下のように56行目のDefaults requirettyコメントアウトした

$ sudo visudo
# Disable "ssh hostname sudo <cmd>", because it will show the password in clear.
#         You have to run "ssh -t hostname sudo <cmd>".
#
#Defaults    requiretty

以上で実行できるはずなので10分ごとにスクリプトを実行するようにする

$ crontab -e
*/10 * * * * python /path/to/script.py

References

RaspberryPiのSDカード寿命対策

システムはRaspbian Jessie Liteを使用した

swapを停止する

swapの使用状況を確認

$ free
             total       used       free     shared    buffers     cached
Mem:        948108     100608     847500       6432       8008      62572
-/+ buffers/cache:      30028     918080
Swap:       102396          0     102396

/etc/fstabに以下の記述があったため

# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that

dphys-swapfileを停止する

$ sudo sysv-rc-conf dphys-swapfile off

再起動して確認

$ sudo reboot

$ free
             total       used       free     shared    buffers     cached
Mem:        948108     166408     781700       6432       7152     128220
-/+ buffers/cache:      31036     917072
Swap:            0          0          0

RAMDiskを使う

/var/logRAMDiskにする際はスクリプトを作る必要があるそうだが,問題がなかったので作成しない

  • /tmp
  • /var/tmp
  • /var/log

をtmpfsでマウントする

$ sudo vim /etc/fstab
proc            /proc           proc    defaults          0       0
/dev/mmcblk0p1  /boot           vfat    defaults          0       2
/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that
tmpfs   /tmp        tmpfs   defaults,noatime    0 0
tmpfs   /var/tmp    tmpfs   defaults,noatime    0 0
tmpfs   /var/log    tmpfs   defaults,noatime    0 0

マウントできるか確認

$ sudo rm -rf /tmp/* && sudo mount /tmp
$ sudo rm -rf /var/tmp/* && sudo mount /var/tmp
$ sudo rm -rf /var/log/* && sudo mount /var/log

$ df -h
...
tmpfs           463M     0  463M   0% /tmp
tmpfs           463M     0  463M   0% /var/tmp
tmpfs           463M     0  463M   0% /var/log

一応,再起動したあとに確認

$ df -h
tmpfs           463M  116K  463M   1% /var/log
tmpfs           463M     0  463M   0% /var/tmp
tmpfs           463M     0  463M   0% /tmp

順番は変わったが,ちゃんと動いてそう

Ubuntu15.10でdockerを動かす

インストール

$ sudo apt-get -y install docker.io

ユーザ設定

dockerグループではないユーザでは以下のようなエラーが出る

$ docker images
FATA[0000] Get http://%2Fvar%2Frun%2Fdocker.sock/v1.18/images/json: dial unix /var/run/docker.sock: connect: permission denied. Are you trying to connect to a TLS-enabled daemon without TLS?

sudoするかdockerを使用するユーザをdockerグループに追加すればいいので
dockerグループに追加する

$ sudo groupadd docker
$ sudo gpasswd -a ${USER} docker
$ sudo systemctl restart docker

$ sudo reboot

dockerを試す

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE

$ docker pull centos
latest: Pulling from centos
47d44cb6f252: Pull complete 
838c1c5c4f83: Pull complete 
5764f0a31317: Pull complete 
60e65a8e4030: Pull complete 
centos:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:8072bc7c66c3d5b633c3fddfc2bf12d5b4c2623f7004d9eed6aae70e0e99fbd7
Status: Downloaded newer image for centos:latest

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              latest              60e65a8e4030        2 weeks ago         196.6 MB

イメージが準備出来たので コンテナを起動

$ docker run -it centos:latest
[root@a7c6fef2e476 /]# whoami
root
[root@a7c6fef2e476 /]# cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core) 
[root@a7c6fef2e476 /]# exit