WordPressのプラグインで認証付きダウンローダー

S3に入れて置いたデータをwordpressのプラグインで認証付きのダウンローダを作れないかとの検討

プラグインは便利なので、Wordpressにログインした人のみがダウンロードできるようにしたい

こんな感じでできました

環境

    • s3にファイルを格納しておく
      wordpressはec2
      プラグインはショートコードでWordpressの固定ページなどに埋め込む
  • 仕様

    http://example.com/downloader?img=dir/file.png&token=session&userid=userid
    
    • img: S3のファイルパス
    • token: wordpressのセッションID
    • userid: wordpressのログインID(ログイン名ではなくWordpress内部でのログイン番号)

    データ

    以下に格納しておく
    s3://example/dir/file.png

    ショートコード

    固定ページをhttp://example.com/downloaderで作成し、以下を埋め込む

    [sample_downloader]
    

    プラグイン

    sample_downloaderディレクトリ以下にmain.phpで作成

    run($userid,$token,$img);
    }
    
    require_once( plugin_dir_path(__FILE__) . 'vendor/autoload.php' );
    add_shortcode('sample_downloader','sample_downloader_func');
    use Aws\S3\S3Client;
    
    
    class SampleDownloader{
        function run($userid,$token,$img){
            $bucket="example";
    
            // 認証
            $wp_token=wp_get_session_token();
            $wp_userid=wp_get_current_user();
            if($userid!=$wp_userid->ID || $wp_token!=$token){
                header("HTTP/1.1 401 Unauthorized");
                return "Authorized Error";
            }
    
            try{
    
                $s3Client = S3Client::factory(array(
                        'version' => 'latest',
                        'region'  => 'ap-northeast-1'
    	        ));
                
                $result = $s3Client->getObject([
                    'Bucket' => $bucket,
                    'Key' => $img
                ]);
                $body=$result["Body"];
    
    
                $fname=basename($img);
                $sfx=pathinfo($fname, PATHINFO_EXTENSION);
                header("HTTP/1.1 200 OK");
    
                // これで本体から出力されるHTMLをクリアする
    	    if (ob_get_length() > 0) {
                    ob_end_clean();
                }
    
                switch($sfx){
                    case "svg":
                        header('Content-Type: image/svg+xml');
                        break;
                    case "png":
                        header('Content-Type: image/pmg');
                        break;
                    case "jpg":
                    case "jpeg":
                        header('Content-Type: image/jpeg');
                        break;
                    case "gif":
                        header('Content-Type: image/gif');
                        break;
                    case "csv":
                        header('Content-Type: text/csv');
                        break;
                    case "json":
                        header('Content-Type: application/json');
                        break;
                    case "pdf":
                        header('Content-Type: application/pdf');
                        break;
                    case "xls":
                        header('Content-Type: application/vnd.ms-excel');
                        break;
                    case "xlsx":
                        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
                        break;
                    case "ppt":
                        header('Content-Type: application/vnd.ms-powerpoint');
                        break;
                    case "pptx":
                        header('Content-Type: application/vnd.openxmlformats-officedocument.presentationml.presentation');
                        break;
                    case "doc":
                        header('Content-Type: application/msword');
                        break;
                    case "docx":
                        header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
                        break;
                    default:
                        header('Content-Type: application/octet-stream');
                }
    
                header('Content-Disposition: attachment; filename='.$fname);
                return $body;
    
            }catch(Exception $e){
                header("HTTP/1.1 500 Internal Server Error");
    	    return "Error";
            }
        }
    }
    ?>
    
    

    デプロイ

    zipファイルを作成しWordpressのプラグインでアップロード
    cd sample_downloader
    composer  require aws/aws-sdk-php
    cd ..
    zip -r sample_downloader.zip sample_downloader/*php sample_downloader/vendor
    

    ubuntuでのtimemachine

    ubuntu22.04でMac用のTimeMachineサーバを立ち上げるための設定メモ

    ネットを調べるといろいろな設定があったのですが,最終的には
    こちらが参考になりました。

    ubuntuというユーザを作成し,このユーザの権限で書き込むことにします

    /etc/samba/smb.confを修正
    globalに以下を追加

    [global]
      fruit:encoding = native
      fruit:metadata = netatalk
      idmap config * : backend = tdb
      vfs objects = catia fruit streams_xattr
    
    

    smb.confの最後にこれを追加

    [timemachine]
      path = /path/to/timemachine
      guest ok = yes
      read only = No
      force user = ubuntu
      fruit:time machine max size = 500G
      fruit:time machine = yes
    

    ubuntuでのsysstat

    悩んでしまったのでメモ

    ubuntuでsysstatを入れるには,

    sudo apt install sysstat
    

    でインストールし,

    sudo vi /etc/default/sysstat
    


    ENABLED=”true”
    にするというもの。
    その後

    sudo /etc/init.d/sysstat restart
    

    で再起動でOK
    というはずだった。。

    しかしこれだけではうまくいきません。

    $ sar
    Cannot open /var/log/sysstat/sa24: No such file or directory
    Please check if data collecting is enabled
    

    こんな感じでエラーが出るだけ。。。

    ようやく解決しました
    上記設定を行った後,これを実行し再起動するだけ

    sudo systemctl enable sysstat
    

    ずいぶん悩んでしまいました。。。

    Windows11のHyper-Vで作成したWindows10クライアント上でGPUを動かす

    概要

    Windows11のゲストOS上でGPUパススルー出来ればいいなと思ったのでメモ。

    方法

    ググるとここなどにやり方を書いているのですが、どうもうまくいかない。
    結局ここのやり方でうまくいきました。

    ゲストOS

    設定は次の通り。

    • 世代は2で作成
    • チェックポイント無効
    • メモリは固定
    • セキュアブート有効

    ファイルコピー

    • ホストOSのC:\Windows\System32\DriverStore\FileRepository\以下にあるnv_dispi.inf_amd64で始まるフォルダをゲストOSのC:\Windows\System32\HostDriverStore\FileRepository\ 以下にコピー
    • ホストOSのC:\Windows\System32\以下にある nvで始まるファイルをゲストOSのC:\Windows\System32\にコピー

    ホスト側からの設定

    ここでいったんゲストOSを停止して、管理者モードで起動したPowerShellコマンドプロンプトから以下を打ち込む

    $vm = "ENTER YOUR VM NAME HERE"
    Remove-VMGpuPartitionAdapter -VMName $vm
    Add-VMGpuPartitionAdapter -VMName $vm
    Set-VM -GuestControlledCacheTypes $true -VMName $vm
    Set-VM -LowMemoryMappedIoSpace 1Gb -VMName $vm
    Set-VM -HighMemoryMappedIoSpace 32GB -VMName $vm
    

    確認

    ゲストOSを再度起動する。デバイスマネージャーからGPUが有効になっていることを確認。
    タスクマネージャーではGPUが見られないので、Terminalなどで半透明にしたものが、透けて見えることで確認

    ベンチマーク

    FinalFantasy XV のベンチマークやってみました。

    ホストOS

    • Windows 11 Pro
    • RTX 2060
    • memory 32G
    • Core i7 12700

    ゲストOS

    • Windows 10 Pro
    • RTX 2060
    • memory 16G
    • Core i7 12700 CPU 10コア

    ベンチマークパラメタ

    • 高画質 2560×1440 ウインドウ
    • ホストOS: 5025
    • ゲストOS: 3960

    若干ゲストOS側が悪くなりましたが、まあ使えるレベルですね

    wordpressのエラー

    2,3日前に,以前作成したWordpressのサイトが,
    エラーで入れなくなったと連絡がありました。

    さくらインターネットで運用しているのですが,サポートに連絡しても
    埒が開かないので,こちらにヘルプ依頼が。

    確かに,Wordpressのログインができないようになっています。
    仕方がないので,SSHログインしてみたところ, dead.letter  が。

    この問題を解決しようとする際、以下の情報を聞かれるかもしれません。
    WordPress バーション5.8.1
    現在のテーマ: Spacious (バージョン 1.9.4)
    現在のプラグイン: Contact Form 7 (バージョン 5.5.1)
    PHP バージョン5.6.40
    
    エラー詳細
    ===============
    エラータイプ E_PARSE が wp-content/plugins/contact-form-7/admin/includes/welcome-panel.php ファイルの 9 行目で発生しました。 エラーメッセージ: syntax error, unexpected 'print' (T_PRINT), expecting identifier (T_STRING)
    

    こんなエラーが出てました。どうやらプラグインの自動アップデートでやられたようです。
    ContactForm7なのでたくさん使われているはずなのに,それほど大きな問題になっていないのはなぜ?

    調べたところどうやら,これが原因。

    さくっと,welcome-panel.phpを修正し

    Line 9: Rename function to public function _print()
    Line 223: Rename to $column->_print();
    

    動きました。

    どうやら,このサイトはphp5.6で運用していたのが原因かもしれません。
    このあとPHP7.4にアップデートしておきました。

    ジョブスケジューラのエラー

    大学のスパコンはジョブ管理システム PBSによってジョブスケジュールを行なっているのですが,
    最近変わったのか,Pythonで並列処理をしようとするとなぜかエラーが出るようになりました。

    エラー

    =>> PBS: job killed: ncpus 20.8 exceeded limit 16
    

    コード

    
    import os
    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    size = comm.Get_size()
    rank = comm.Get_rank()
    name = MPI.Get_processor_name()
    
    import csv
    source=[]
    target=[]
    with open("test.csv","r",encoding="utf-8") as f:
        cdata=csv.reader(f,delimiter=",")
        for i,items in enumerate(cdata):
            if i==0:
                continue
            source.append(items[0])
            target.append(items[1])
    
    data_lst=[]
    for src,tgt in zip(source,target):
        data_lst.append((src,tgt))
    
    # http://muscle199x.blog.fc2.com/blog-entry-70.html?sp
    def split_list(lst, n):
        list_size = len(lst)
        a = list_size // n
        b = list_size % n
        return [lst[i * a + (i if i < b else b):(i + 1) * a + (i + 1 if i < b else b)] for i in range(n)]
    
    from transformers import MT5ForConditionalGeneration, MT5Model, MT5EncoderModel, T5Tokenizer
    
    tokenizer = T5Tokenizer.from_pretrained("google/mt5-base")
    model = MT5ForConditionalGeneration.from_pretrained("google/mt5-base")
    if rank == 0:
        data = split_list(data_lst, size)
    else:
        data = None
    
    def calc(my_data):
        res=[]
    
        for src,tgt in my_data:
            inputs = tokenizer.encode("summarize: " + src, return_tensors='pt', max_length=512, truncation=True)
            summary_ids = model.generate(inputs, max_length=50, min_length=10, length_penalty=5., num_beams=2)
            summary = tokenizer.decode(summary_ids[0])
            res.append((src,tgt,summary))
        return res
    
    my_data = comm.scatter(data, root=0)
    
    for r in range(comm.size):
        if rank == r:
            print("scatter.[%d] %d" % (rank, len(my_data)))
        comm.Barrier()
    
    my_res = calc(my_data)
    def flatten(l):
        try:
            return ([item for sublist in l for item in sublist])
        except:
            import traceback
            traceback.print_exc()
            print(l)
    
    # gather
    res = MPI.COMM_WORLD.gather(my_res, root=0)
    
    if rank == 0:
        res=flatten(res)
        for src,tgt,summary in res:
            print(f"source:{src}")
            print(f"target:{tgt}")
            print(f"summary:{summary}")
            print("-----")
    
    
    

    これで実行スクリプトはこんな感じ

    #!/usr/bin/bash
    #PBS -N sample
    #PBS -j oe 
    #PBS -l select=1:ncpus=64:mpiprocs=64
    #PBS -q SINGLE
    NPROCS=`cat $PBS_NODEFILE|wc -l`
    cd ${PBS_O_WORKDIR}
    mpirun -np ${NPROCS} python mt5.py
    

    解決方法

    ここにありました。

    簡単に解決するためには,Pythonコードの一番上に

    
    import os
    os.environ["MKL_NUM_THREADS"] = "1"
    os.environ["NUMEXPR_NUM_THREADS"] = "1"
    os.environ["OMP_NUM_THREADS"] = "1"
    
    

    これを入れるだけ。Numpyを呼ぶ前に呼ぶ必要があるので,シェルスクリプトで呼んでもいいですし,Pythonの一番上で環境変数に設定してやってもいいです。

    備忘録,JAISTのスパコン使い方

    Jaistには3つスパコンがあります。
    学生でもどれも自由に使えます。
    この辺りが他の大学と違うところでしょうか?

    今回はつい最近と言っても1年くらい経ちますが,リニューアルされた
    スパコンKagayakiでのPythonでのMPIの使い方。

    公式にはPythonはサポートしてません。

    Anaconda

    いろいろ試したのですが,これが一番。
    スーパーユーザ権限がないのでローカルにインストールし,パスを設定します。

    $ cat ~/.bashrc
    # >>> conda initialize >>>
    # !! Contents within this block are managed by 'conda init' !!
    __conda_setup="$('~/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
    if [ $? -eq 0 ]; then
        eval "$__conda_setup"
    else
        if [ -f "~/anaconda3/etc/profile.d/conda.sh" ]; then
            . "~/anaconda3/etc/profile.d/conda.sh"
        else
            export PATH="~/anaconda3/bin:$PATH"
        fi
    fi
    unset __conda_setup
    # <<< conda initialize <<<
    

    実行コード 

    mpi.py

    
    #!/bin/env python
    # coding:utf-8
    
    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    size = comm.Get_size()
    rank = comm.Get_rank()
    name = MPI.Get_processor_name()
    print(f"size={size},rank={rank},name={name}")
    
    

    mpi.sh

    . ~/.bashrc
    if [ ! -d venv ];then
        python -m venv venv
    fi
    . venv/bin/activate
    LOG=${0%.*}.log
    pip install -r requirements.txt > $LOG
    
    mpirun -np 4 python mpi.py > mpi.log 2>&1
    

    デフォルトではCSHなのでCSHでキックコードを作成し,中ではBashを使います

    PBS_mpi.sh

    #PBS -N mpi
    #PBS -j oe 
    #PBS -l select=1
    #PBS -q SINGLE
    
    
    source /etc/profile.d/modules.csh
    
    module purge
    module load openmpi
    module load intel
    
    
    cd ${PBS_O_WORKDIR}
    
    bash mpi.sh
    

    実行

    $ qsub PBS_mpi.sh
    

    結果

    $ cat mpi.log
    size=4,rank=1,name=spcc-250
    size=4,rank=3,name=spcc-250
    size=4,rank=0,name=spcc-250
    size=4,rank=2,name=spcc-250
    

    さくらインターネットのメーリングリストFML4で添付ファイル禁止

    ちょっと,苦労したのでメモ

    さくらインターネットのメーリングリストはfml4を使用している。
    このメーリングリストでの設定ファイルは,各メーリングリストごとのconfig.phで行う。

    ちなみに,config.phをいじると,コントロールパネルから設定を変更すると上書きされてしまうので注意

    % cd ~/fml/spool/ml/メーリングリスト名/
    

    config.phに追記。最後の1;の直上に書く

    $USE_DISTRIBUTE_FILTER = 1;
    $DISTRIBUTE_FILTER_HOOK .= q#
        if ($e{'Body'} =~ /filename=.*/i) {
            return 'Do not post messages with attachments';
        }
    #;
    

    要は,filename属性があったときには拒否するということ

    ネットで調べた下記方法では,HTMLメールまでおかしくなってしまうので,NG

    &ADD_CONTENT_HANDLER('multipart/.*','text/plain','allow');
    &ADD_CONTENT_HANDLER('multipart/.*','text/html','strip');
    &ADD_CONTENT_HANDLER('multipart/.*','*./.*','strip');
    

    いろいろ調べてしまった。

    LinkstationにDebianを導入し,Timemachineサーバにする

    LS-V1.5TLというBuffaloのNASをずいぶん前に買ったのですが,あまり活用ができていなかったので,
    Linuxを入れることにしました。

    元々のNASのファームウエアにもTimemachineサーバはついているのですが,もう古いマシンなので壊れても
    いいという感じでLinux化し遊ぶことに。

    Linux化

    こちらの記事がわかりやすいです。
    一度分解し,HDDを取りだすのが面倒ですが,それさえ終われば,快適なLinux化ができます。
    当然のように,分解の際にほぼ全てのつめを折ってしまいましたが気にしない。

    timemachine

    手順などはあちらこちらに書いているのですが

    avahi

    ここを参考に,avahiをインストールします。debianのパッケージがあるのでそのまま使います

    sudo apt install avahi-daemon
    

    設定はこんな感じ

    /etc/avahi/services/timemachine.service

    
    
    
     %h
     
       _smb._tcp
       445
     
     
       _adisk._tcp
       dk0=adVN=TimeMachine,adVF=0x82
     
    
    

    samba

    sambaをインストールします。Samba上で使うみたいです

    sudo apt install samba
    

    sambauser

    timemachineとユーザを作ります

    sudo adduser timemachine
    sudo password -a timemachine
    sudo smbpasswd timemachine
    

    timemachine directory

    バックアップ用ディレクトリを作成します

    sudo mkdir /mnt/timemachine
    sudo chown -R timemachine:timemachine /mnt/timemachine
    

    smb.conf

    ここでポイントは,通常のSambaユーザとは別にし,Finderなどからはいじれないようにします。
    同時にアクセスするとエラーになるための回避です。

    [TimeMachine]
       path = /mnt/timemachine
       browsable = yes
       writeable = yes
       create mode = 0664     #tried turning this off, no fix
       directory mode = 0777  #tried turning this off, no fix
       vfs objects = catia fruit streams_xattr
       fruit:aapl = yes
       fruit:time machine = yes
       #guest ok = yes
       fruit:time machine max size =1T  #tried turning this off, no fix
       inherit acls = yes
    

    restart

    リスタートします

    sudo /etc/init.d/smbd restart
    sudo /etc/init.d/avahi-daemon restart
    

    これで使えると思います。

    Mac OS 11.4でpip pdftotext

    M1Macで何故か素直に入らなかったのでメモ

    環境

    • M1 Mac book Air
    • OS: 11.4
    • python: Intel miniconda3
    • python version:3.8.5

    手順

    brewでPdftotextを入れる

    brew install pdftotext
    

    ~/.zshrcに追加

    export C_INCLUDE_PATH=/opt/homebrew/Cellar/poppler/21.05.0/include:$C_INCLUDE_PATH
    export CPATH=$C_INCLUDE_PATH
    export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/opt/homebrew/Cellar/poppler/21.05.0/lib # どうもこれは効かないらしい
    

    pipでpdftotextライブラリを入れる 

    pip install pdftotext --global-option=build_ext --global-option="-L/opt/homebrew/Cellar/poppler/21.05.0/lib"