2021年6月10日 星期四

How to Set Up WebDAV With Lighttpd



WebDAV stands for Web-based Distributed Authoring and Versioning and is a set of extensions to the HTTP protocol that allow users to directly edit files on the lighttpd server so that they do not need to be downloaded/uploaded via FTP. Of course, WebDAV can also be used to upload and download files.

Installing WebDAV

You can install lighttpd (if it’s not already installed), the lighttpd WebDAV module and the apache2-utils package (which contains the tool htpasswd which we will need later on to generate a password file for the WebDAV share) as follows:

1
sudo apt-get install lighttpd lighttpd-mod-webdav apache2-utils

Afterwards, we must make sure that the directory /var/run/lighttpd is owned by the www-data user and group. This directory will contain an SQLite database needed by WebDAV:

1
sudo chown www-data:www-data /var/run/lighttpd/

Next, we enable the modules mod_auth and mod_webdav:

1
2
sudo lighty-enable-mod auth 
sudo lighty-enable-mod webdav

Reload lighttpd afterwards:

1
sudo systemctl restart lighttpd

Creating A Virtual Host

I will now create a lighttpd vhost (www.example.com) in the directory /var/www/web1/web. If you already have a vhost for which you’d like to enable WebDAV, you must adjust this tutorial to your situation.

First, we create the directory /var/www/web1/web and make the lighttpd user (www-data) the owner of that directory:

1
2
sudo mkdir -p /var/www/web1/web 
sudo chown www-data:www-data /var/www/web1/web

Then we open /etc/lighttpd/lighttpd.conf and add the following vhost to the end of the file:

1
2
3
4
5
:~$ vim /etc/lighttpd/lighttpd.conf
[...]
$HTTP["host"] == "www.shixuen.com" {
server.document-root = "/var/www/web1/web"
}

Afterwards we restart lighttpd:

1
:~$ sudo systemctl restart lighttpd

Configure The Virtual Host For WebDAV

Now we create the WebDAV password file /var/www/web1/passwd.dav with the user test (the -c switch creates the new password file):

1
:~$ htpasswd -c /var/www/web1/passwd.dav test

You will be asked to type in a password for the user test.

(Please don’t use the -c switch if /var/www/web1/passwd.dav is already existing because this will recreate the file from scratch, meaning you lose all users in that file!)

Now we change the permissions of the /var/www/web1/passwd.dav file so that only root and the members of the www-data group can access it:

1
2
:~$ chown root:www-data /var/www/web1/passwd.dav
:~$ chmod 640 /var/www/web1/passwd.dav

Now we modify our vhost in /etc/lighttpd/lighttpd.conf so that it looks as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
:~$ vim /etc/lighttpd/lighttpd.conf
$HTTP["host"] == "www.shixuen.com" {
server.document-root = "/var/www/web1/web"
alias.url = ( "/webdav" => "/var/www/web1/web" )
$HTTP["url"] =~ "^/webdav($|/)" {
dir-listing.activate = "enable"
dir-listing.encoding = "utf-8"
webdav.activate = "enable"
webdav.is-readonly = "disable"
webdav.sqlite-db-name = "/var/run/lighttpd/lighttpd.webdav_lock.db"
auth.backend = "htpasswd"
auth.backend.htpasswd.userfile = "/var/www/web1/passwd.dav"
auth.require = ( "" =>
(
"method" => "basic",
"realm" => "webdav",
"require" => "valid-user"
)
)
}
}

The alias.url directive makes ( together with $HTTP[“url”] =~ “^/webdav($|/)” ) that when you call /webdavWebDAV is invoked, but you can still access the whole document root of the vhost. All other URLs of that vhost are still “normal” HTTP.

Restart lighttpd afterwards:

1
:~$ sudo systemctl restart lighttpd

Test WebDAV

Browser

1
:~$ firefox http://www.shixuen.com/webdav

Cadaver - WebDAV client

We will now install cadaver, a command-line WebDAV client:

1
:~$ apt-get install cadaver

To test if WebDAV works, type:

1
:~$ cadaver http://www.shixuen.com/webdav/

You should be prompted for a user name. Type in test and then the password for the user test. If all goes well, you should be granted access which means WebDAV is working ok. Type quit to leave the WebDAV shell:

1
2
3
4
5
6
7
root@server1:~# cadaver http://www.shixuen.com/webdav/
Authentication required for webdav on server 'www.shixuen.com':
Username: test
Password:
dav:/webdav/> quit
Connection to 'www.shixuen.com' closed.
root@server1:~#

Modules

lighttpd docs

mod_auth

lighttpd_auth module

Supported Methods

lighttpd supports both authentication methods described by RFC 2617:

basic

The Basic method transfers the username and the password in cleartext over the network (base64 encoded) and might result in security problems if not used in conjunction with a crypted channel between client and server.

digest

The Digest method only transfers a hashed value over the network which performs a lot of work to harden the authentication process in insecure networks.

Backends

Depending on the method lighttpd provides various way to store the credentials used for the authentication.

  • For basic auth:
    • plain
    • htpasswd
    • htdigest
    • ldap
    • gssapi
    • mysql
    • pam
    • sasl
  • For digest auth:
    • plain
    • htdigest

References:

---- The End  Thanks ----

 


lighttpd.conf


2021年6月9日 星期三

PHP實作websocket


GITHUB: https://github.com/jash-git/PHP-websocket


 websocket_s.php

<?php
error_reporting(E_ALL);
set_time_limit(0);// 设置超时时间为无限,防止超时
date_default_timezone_set('Asia/Taipei');

class WebSocket {
    const LOG_PATH = '/tmp/';
    const LISTEN_SOCKET_NUM = 9;

    /**
     * @var array $sockets
     *    [
     *      (int)$socket => [
     *                        info
     *                      ]
     *      ]
     *  todo 解释socket与file号对应
     */
    private $sockets = [];
    private $master;

    public function __construct($host, $port) {
        try {
            $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            // 设置IP和端口重用,在重启服务器后能重新使用此端口;
            socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
            // 将IP和端口绑定在服务器socket上;
            socket_bind($this->master, $host, $port);
            // listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接,其中的能存储的请求不明的socket数目。
            socket_listen($this->master, self::LISTEN_SOCKET_NUM);
        } catch (\Exception $e) {
            $err_code = socket_last_error();
            $err_msg = socket_strerror($err_code);

            $this->error([
                'error_init_server',
                $err_code,
                $err_msg
            ]);
        }

        $this->sockets[0] = ['resource' => $this->master];
        $pid = posix_getpid();
        $this->debug(["server: {$this->master} started,pid: {$pid}"]);

        while (true) {
            try {
                $this->doServer();
            } catch (\Exception $e) {
                $this->error([
                    'error_do_server',
                    $e->getCode(),
                    $e->getMessage()
                ]);
            }
        }
    }

    private function doServer() {
        $write = $except = NULL;
        $sockets = array_column($this->sockets, 'resource');
        $read_num = socket_select($sockets, $write, $except, NULL);
        // select作为监视函数,参数分别是(监视可读,可写,异常,超时时间),返回可操作数目,出错时返回false;
        if (false === $read_num) {
            $this->error([
                'error_select',
                $err_code = socket_last_error(),
                socket_strerror($err_code)
            ]);
            return;
        }

        foreach ($sockets as $socket) {
            // 如果可读的是服务器socket,则处理连接逻辑
            if ($socket == $this->master) {
                $client = socket_accept($this->master);
                // 创建,绑定,监听后accept函数将会接受socket要来的连接,一旦有一个连接成功,将会返回一个新的socket资源用以交互,如果是一个多个连接的队列,只会处理第一个,如果没有连接的话,进程将会被阻塞,直到连接上.如果用set_socket_blocking或socket_set_noblock()设置了阻塞,会返回false;返回资源后,将会持续等待连接。
                if (false === $client) {
                    $this->error([
                        'err_accept',
                        $err_code = socket_last_error(),
                        socket_strerror($err_code)
                    ]);
                    continue;
                } else {
                    self::connect($client);
                    continue;
                }
            } else {
                // 如果可读的是其他已连接socket,则读取其数据,并处理应答逻辑
                $bytes = @socket_recv($socket, $buffer, 2048, 0);
                if ($bytes < 9) {
                    $recv_msg = $this->disconnect($socket);
                } else {
                    if (!$this->sockets[(int)$socket]['handshake']) {
                        self::handShake($socket, $buffer);
                        continue;
                    } else {
                        $recv_msg = self::parse($buffer);
                    }
                }
                array_unshift($recv_msg, 'receive_msg');
                $msg = self::dealMsg($socket, $recv_msg);

                $this->broadcast($msg);
            }
        }
    }

    /**
     * 将socket添加到已连接列表,但握手状态留空;
     *
     * @param $socket
     */
    public function connect($socket) {
        socket_getpeername($socket, $ip, $port);
        $socket_info = [
            'resource' => $socket,
            'uname' => '',
            'handshake' => false,
            'ip' => $ip,
            'port' => $port,
        ];
        $this->sockets[(int)$socket] = $socket_info;
        $this->debug(array_merge(['socket_connect'], $socket_info));
    }

    /**
     * 客户端关闭连接
     *
     * @param $socket
     *
     * @return array
     */
    private function disconnect($socket) {
        $recv_msg = [
            'type' => 'logout',
            'content' => $this->sockets[(int)$socket]['uname'],
        ];
        unset($this->sockets[(int)$socket]);

        return $recv_msg;
    }

    /**
     * 用公共握手算法握手
     *
     * @param $socket
     * @param $buffer
     *
     * @return bool
     */
    public function handShake($socket, $buffer) {
        // 获取到客户端的升级密匙
        $line_with_key = substr($buffer, strpos($buffer, 'Sec-WebSocket-Key:') + 18);
        $key = trim(substr($line_with_key, 0, strpos($line_with_key, "\r\n")));

        // 生成升级密匙,并拼接websocket升级头
        $upgrade_key = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));// 升级key的算法
        $upgrade_message = "HTTP/1.1 101 Switching Protocols\r\n";
        $upgrade_message .= "Upgrade: websocket\r\n";
        $upgrade_message .= "Sec-WebSocket-Version: 13\r\n";
        $upgrade_message .= "Connection: Upgrade\r\n";
        $upgrade_message .= "Sec-WebSocket-Accept:" . $upgrade_key . "\r\n\r\n";

        socket_write($socket, $upgrade_message, strlen($upgrade_message));// 向socket里写入升级信息
        $this->sockets[(int)$socket]['handshake'] = true;

        socket_getpeername($socket, $ip, $port);
        $this->debug([
            'hand_shake',
            $socket,
            $ip,
            $port
        ]);

        // 向客户端发送握手成功消息,以触发客户端发送用户名动作;
        $msg = [
            'type' => 'handshake',
            'content' => 'done',
        ];
        $msg = $this->build(json_encode($msg));
        socket_write($socket, $msg, strlen($msg));
        return true;
    }

    /**
     * 解析数据
     *
     * @param $buffer
     *
     * @return bool|string
     */
    private function parse($buffer) {
        $decoded = '';
        $len = ord($buffer[1]) & 127;
        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }

        return json_decode($decoded, true);
    }

    /**
     * 将普通信息组装成websocket数据帧
     *
     * @param $msg
     *
     * @return string
     */
    private function build($msg) {
        $frame = [];
        $frame[0] = '81';
        $len = strlen($msg);
        if ($len < 126) {
            $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
        } else if ($len < 65025) {
            $s = dechex($len);
            $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
        } else {
            $s = dechex($len);
            $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
        }

        $data = '';
        $l = strlen($msg);
        for ($i = 0; $i < $l; $i++) {
            $data .= dechex(ord($msg{$i}));
        }
        $frame[2] = $data;

        $data = implode('', $frame);

        return pack("H*", $data);
    }

    /**
     * 拼装信息
     *
     * @param $socket
     * @param $recv_msg
     *          [
     *          'type'=>user/login
     *          'content'=>content
     *          ]
     *
     * @return string
     */
    private function dealMsg($socket, $recv_msg) {
        $msg_type = $recv_msg['type'];
        $msg_content = $recv_msg['content'];
        $response = [];

        switch ($msg_type) {
            case 'login':
                $this->sockets[(int)$socket]['uname'] = $msg_content;
                // 取得最新的名字记录
                $user_list = array_column($this->sockets, 'uname');
                $response['type'] = 'login';
                $response['content'] = $msg_content;
                $response['user_list'] = $user_list;
                break;
            case 'logout':
                $user_list = array_column($this->sockets, 'uname');
                $response['type'] = 'logout';
                $response['content'] = $msg_content;
                $response['user_list'] = $user_list;
                break;
            case 'user':
                $uname = $this->sockets[(int)$socket]['uname'];
                $response['type'] = 'user';
                $response['from'] = $uname;
                $response['content'] = $msg_content;
                break;
        }

        return $this->build(json_encode($response));
    }

    /**
     * 广播消息
     *
     * @param $data
     */
    private function broadcast($data) {
        foreach ($this->sockets as $socket) {
            if ($socket['resource'] == $this->master) {
                continue;
            }
            socket_write($socket['resource'], $data, strlen($data));
        }
    }

    /**
     * 记录debug信息
     *
     * @param array $info
     */
    private function debug(array $info) {
        $time = date('Y-m-d H:i:s');
        array_unshift($info, $time);

        $info = array_map('json_encode', $info);
        file_put_contents(self::LOG_PATH . 'websocket_debug.log', implode(' | ', $info) . "\r\n", FILE_APPEND);
    }

    /**
     * 记录错误信息
     *
     * @param array $info
     */
    private function error(array $info) {
        $time = date('Y-m-d H:i:s');
        array_unshift($info, $time);

        $info = array_map('json_encode', $info);
        file_put_contents(self::LOG_PATH . 'websocket_error.log', implode(' | ', $info) . "\r\n", FILE_APPEND);
    }
}

$ws = new WebSocket("192.168.0.236", "8080");
?>

websocket_c.php

<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<style>
p {
text-align: left;
padding-left: 20px;
}
</style>
</head>
<body>
<div style="width: 800px;height: 600px;margin: 30px auto;text-align: center">
<h1>websocket聊天室</h1>
<div style="width: 800px;border: 1px solid gray;height: 300px;">
<div style="width: 200px;height: 300px;float: left;text-align: left;">
<p><span>当前在线:</span><span id="user_num">0</span></p>
<div id="user_list" style="overflow: auto;">

</div>
</div>
<div id="msg_list" style="width: 598px;border:  1px solid gray; height: 300px;overflow: scroll;float: left;">
</div>
</div>
<br>
<textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
<input type="button" value="发送" onclick="send()">
</div>
</body>
</html>

<script type="text/javascript">
    // 存储用户名到全局变量,握手成功后发送给服务器
    var uname = prompt('请输入用户名', 'user' + uuid(8, 16));
    var ws = new WebSocket("ws://192.168.0.236:8080");
    ws.onopen = function () {
        var data = "系统消息:建立连接成功";
        listMsg(data);
    };

    /**
     * 分析服务器返回信息
     *
     * msg.type : user 普通信息;system 系统信息;handshake 握手信息;login 登陆信息; logout 退出信息;
     * msg.from : 消息来源
     * msg.content: 消息内容
     */
    ws.onmessage = function (e) {
        var msg = JSON.parse(e.data);
        var sender, user_name, name_list, change_type;

        switch (msg.type) {
            case 'system':
                sender = '系统消息: ';
                break;
            case 'user':
                sender = msg.from + ': ';
                break;
            case 'handshake':
                var user_info = {'type': 'login', 'content': uname};
                sendMsg(user_info);
                return;
            case 'login':
            case 'logout':
                user_name = msg.content;
                name_list = msg.user_list;
                change_type = msg.type;
                dealUser(user_name, change_type, name_list);
                return;
        }

        var data = sender + msg.content;
        listMsg(data);
    };

    ws.onerror = function () {
        var data = "系统消息 : 出错了,请退出重试.";
        listMsg(data);
    };

    /**
     * 在输入框内按下回车键时发送消息
     *
     * @param event
     *
     * @returns {boolean}
     */
    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }

    /**
     * 发送并清空消息输入框内的消息
     */
    function send() {
        var msg_box = document.getElementById("msg_box");
        var content = msg_box.value;
        var reg = new RegExp("\r\n", "g");
        content = content.replace(reg, "");
        var msg = {'content': content.trim(), 'type': 'user'};
        sendMsg(msg);
        msg_box.value = '';
        // todo 清除换行符
    }

    /**
     * 将消息内容添加到输出框中,并将滚动条滚动到最下方
     */
    function listMsg(data) {
        var msg_list = document.getElementById("msg_list");
        var msg = document.createElement("p");

        msg.innerHTML = data;
        msg_list.appendChild(msg);
        msg_list.scrollTop = msg_list.scrollHeight;
    }

    /**
     * 处理用户登陆消息
     *
     * @param user_name 用户名
     * @param type  login/logout
     * @param name_list 用户列表
     */
    function dealUser(user_name, type, name_list) {
        var user_list = document.getElementById("user_list");
        var user_num = document.getElementById("user_num");
        while(user_list.hasChildNodes()) {
            user_list.removeChild(user_list.firstChild);
        }

        for (var index in name_list) {
            var user = document.createElement("p");
            user.innerHTML = name_list[index];
            user_list.appendChild(user);
        }
        user_num.innerHTML = name_list.length;
        user_list.scrollTop = user_list.scrollHeight;

        var change = type == 'login' ? '上线' : '下线';

        var data = '系统消息: ' + user_name + ' 已' + change;
        listMsg(data);
    }

    /**
     * 将数据转为json并发送
     * @param msg
     */
    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }

    /**
     * 生产一个全局唯一ID作为用户名的默认值;
     *
     * @param len
     * @param radix
     * @returns {string}
     */
    function uuid(len, radix) {
        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
        var uuid = [], i;
        radix = radix || chars.length;

        if (len) {
            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
        } else {
            var r;

            uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
            uuid[14] = '4';

            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | Math.random() * 16;
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                }
            }
        }

        return uuid.join('');
    }
</script>

資料來源: https://www.cnblogs.com/zhenbianshu/p/6111257.html

http://jashliao.eu/wordpress/2020/02/10/php%E5%AF%A6%E4%BD%9Cwebsocket/

2021年6月7日 星期一

Setup first in a new linux (yocto)

  • lighttpd /etc/lighttp.conf
    • enable fastcgi
    • change php cgi path
  • php.ini   /etc/php/apache2-php7/php.ini
    • post_max_size = 200M
    • upload_tmp_dir = "/tmp/" //要注意磁碟空間
    • upload_max_filesize = 500M
    • memory_limit = 512M; 記憶體用量上限
    • max_execution_time = 600 ; PHP 指令稿執行時間上限(秒)
    • max_input_time = 600; PHP 指令稿解析輸入資料時間上限(秒)
    • default_socket_timeout = 600; socket 等待逾時(秒)
  • Ethernet
    • udhcpc -i eth0
    • echo "nameserver 192.168.0.1" > /etc/resolv.conf
  • wpa_supplicant   /etc/wpa_supplicant.conf
    • wpa_passphrase "TP-Link_CDF0"  "1234567890" > /etc/wpa_supplicant.conf
    • wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf
    • udhcpc -i wlan0
    • echo "nameserver 192.168.0.1" > /etc/resolv.conf
  • Web base file manager tinyfilemanager.php
    • https://github.com/prasathmani/tinyfilemanager
    • wget http://raw.githubusercontent.com/prasathmani/tinyfilemanager/master/tinyfilemanager.php file.php
  • Web base SQLite manager   phpliteadmin
    • https://www.phpliteadmin.org/download/

Get System Status
  • Service status
    • systemctl -t service
  • Network status
    • ip r
    • ip a s eth0 |grep state
    • ip -s link show eth0


    • sudo ethtool eno1 | grep -i 'Link det'
    • nmcli device status

2021年6月4日 星期五

Linux啟動流程rcN.d rcS.d rc.local

Linux嵌入式相關項目走到最後很難避開要開機自啟一些應用程式或者腳本等,最近也在幫助同事做這個事情,以前自己玩板子的時候都是較為隨便的在/etc/rc.local中添加就可以了,但是專案的話還是走標準一些,不僅為了自己以後的維護,也為了以後同事的維護。所以分享下面的文章

推薦:

啟動腳本和service、chkconfig:http://blog.csdn.net/taiyang1987912/article/details/41698817

轉自http://blog.csdn.net/listener_ri/article/details/45128569

當前系統環境為:Linux mint mate 17.1(基於ubuntu14.04的衍生版)

首先

/etc/rc*.d資料夾中的指令檔的連結目標為:/etc/init.d資料夾下的腳本(*為系統運行級別‘數位’和字母:S,系統優先運行rcS.d目錄下的腳本,然後運行rcN.d下的腳本)

/etc/inittab: inittab為系統的PID=1的進程,決定這系統啟動調用哪些啟動指令檔

1.Linux系統主要通過以下步驟啟動

  1. 啟動Boot Manager
  2. 載入系統內核,啟動init進程, init進程是Linux的根進程,所有的系統進程都是它的子進程。
  3. init進程讀取“/etc/inittab”檔中的信進入inittab中預設的運行級別,按順序運行該運行級別對應資料夾(init*.d)下的腳本。腳本通常“start”參數啟動,並指向一個系統中的程式。通常情況下,“/etc/rcS.d/”目錄下的啟動腳本首先被執行,然後是“/etc/rcN.d/”目錄。例如您設定的運行級別為3,那麼它對應的啟動目錄為“/etc/rc3.d/”
  4. 根據“/etc/rcS.d/”資料夾中對應的腳本啟動Xwindow服務“xorg”      XwindowLinux下的圖形化使用者介面系統。
  5. 啟動登錄管理器,等待用戶登錄

1.1.系統服務

在運行級別對應的資料夾中,您可以看到許多檔案名以“S##”和“K##”起始的啟動腳本連結

init 進程將以“start”為參數,按檔案名循序執行所有以“S##”起始的腳本。腳本名稱中的數字越小,它將被越早執行。

例如在 “/etc/rc2.d/”資料夾中,“S13gdm”檔案名中的數字小於“S23xinetd”,“S13gdm”將比“S23xinetd”先執行。

如果一個腳本連結,以“K##”起始,表示它將以“stop”參數被執行。如果相應服務沒有啟動,則不執行該腳本。

1.2.手動控制服務

你可以手動運行帶有以下參數的啟動腳本,來控制系統服務。
start 啟動
stop 停止
restart 重啟

例如:
/etc/rc2.d/K20powernowd start
有 時您並不清楚當前運行級別,該運行級別下未必有相應腳本;而且此類腳本的前三位元字元並不固定,不便於記憶。

這時,您可以直接使用 “/etc/init.d/”資料夾中的啟動腳本(因為“/etc/rcX.d/”中的啟動腳本都是連結到“/etc/init.d/”資料夾下相應腳本)

具體都有那些系統服務腳本,可以看另一篇文章:《ubuntu中init.d資料夾中常用系統服務腳本》

2.Ubuntu系統架構關於啟動項大致分為四類,每一類都分為系統級和使用者級

  • 第一類upstart,或者叫job,由init管理,設定檔目錄/etc/init~/.init
  • 第二類叫service,由rc.d管理,設定檔目錄/etc/init.d,以及/etc/rc.local文件
  • 第三類叫cron,由contab管理,使用crontab進行配置
  • 第四類叫startup,由xdg管理,設定檔目錄/etc/xdg/autostart,以及~/.config/autostart

upstart任務適用於runlevel<5的腳本和程式,service任務適用於runlevel<=5的任務,cron任務則不一定,而startup一般工作在runlevel=5,也就是桌面級。

對於普通用戶而言,你的桌面級應用應該使用startup,服務級應用(比如某些功能性的軟體腳本)應該使用service,常規性配置可以使用cron,而與啟動順序有關的最好使用upstart。

2.1. 開機啟動時自動運行程式

Linux載入後, 它將初始化硬體和設備驅動, 然後運行第一個進程init。init根據設定檔繼續引導過程,啟動其它進程。通常情況下,修改放置在

  • /etc/rcN.d
  • /etc/rcS.d

目錄下的指令檔,可以使init自動啟動其它程式。例如:編輯/etc/rcS.d/rc.local(也就是/etc/rc.local,因為rcS.d連結目標為/etc) 檔(該檔通常是系統最後啟動的腳本),

在檔最末加上一行“xinit”或“startx”,可以在開機啟動後直接進入X-Window。

2.2. 登錄時自動運行程式

使用者登錄時,bash先自動執行系統管理員建立的全域登錄script 

/ect/profile(大多在此檔中設置環境變數,它是整個桌面環境使用的一個shell進程,也就是登錄shell

>>>在linux中的shell可以分為:登錄shell,非登錄互動式shell,非登錄非互動式shell(執行shell腳本),

>>>具體可以查看另一篇文章:《 linux中各個shell設定檔的作用域和啟動時間 》

然後bash在使用者起始目錄下按順序查找三個特殊檔中的一個:

  • /.bash_profile
  • /.bash_login
  • /.profile

但只執行最先找到的一個。因此,只需根據實際需要在上述檔中加入命令就可以實現用戶登錄時自動運行某些程式(類似於DOS下的Autoexec.bat)。

2.3. 退出登錄時自動運行程式

退出登錄時,bash自動執行個人的退出登入指令檔

  • /.bash_logout

例如,在/.bash_logout中加入命令“tar -cvzf c.source.tgz *.c”,則在每次退出登錄時自動執行 “tar” 命令備份 *.c 文件。

2.4. 定期自動運行程式

Linux有一個稱為crond的守護程式,主要功能是週期性地檢查 /var/spool/cron目錄下的一組命令檔的內容,並在設定的時間執行這些檔中的命令。用戶可以通過crontab 命令來建立、修改、刪除這些命令檔。

例如,建立檔crondFile,內容為“00 9 23 Jan * HappyBirthday”,運行“crontabcronFile”命令後,每當元月23日上午9:00系統自動執行“HappyBirthday”的程式(“*”表示不管當天是星期幾)。

2.5. 定時自動運行程式一次

定時執行命令at 與crond 類似(但它只執行一次):命令在給定的時間執行,但不自動重複。at命令的一般格式為:at [ -f file ] time ,在指定的時間執行file檔中所給出的所有命令。也可直接從鍵盤輸入命令:

$ at 12:00
at>mailto Roger -s ″Have a lunch″ < plan.txt
at>Ctr-D
Job 1 at 20001109 12:00

 

2000-11-09 12:00時候自動發一標題為“Have a lunch”,內容為plan.txt檔內容的郵件給Roger.

3.Ubuntu下添加開機啟動腳本

方式1rc.local

Ubuntu開機之後會執行/etc/rc.local檔中的腳本,所以我們可以直接在/etc/rc.local中添加啟動腳本。

當然要添加到語句:exit 0 前面才行。

方式2rcN.d

如果要添加為開機啟動執行的指令檔,可先將腳本複製或者軟連接到/etc/init.d/目錄下,然後用:update-rc.d xxx defaults NN命令(NN為啟動順序),將腳本添加到初始化執行的佇列中去。

注意如果腳本需要用到網路,則NN需設置一個比較大的數字,如98 。

  • 將腳本設為可執行許可權,並拷貝至/etc/init.d
  • /etc/init.d路徑下執行update-rc.d script-name start 98 5 . 注:98為順序,5rc5.d,符號.不要忘記
  • 這樣會在會在/etc/rc5.d/下面創建1個符號連結,有必要在腳本的前段加一些provider/start-default等說明,否則報警,在Ubuntu上測試成功

 資料來源:https://b8807053.pixnet.net/blog/post/348858208-%5B%E5%95%9F%E5%8B%95%5Dlinux%E5%95%9F%E5%8B%95%E6%B5%81%E7%A8%8Brcn.d-rcs.d-rc.local%E7%AD%89

2021年6月3日 星期四

lighttpd.conf setting for https ssl


在編譯 lighttpd 時,
要在設定時,加上 --with-openssl,這樣才會使用 openssl 。
如下:
./configure --with-openssl
只需要在 lighttpd.conf 裡加上,以下兩行,
ssl.engine = "enable"
ssl.pemfile = "server.pem"
至於,server.pem 是要怎麼製作出來的,
就要靠 openssl 去產生,如下:
openssl req -new -nodes -x509 -keyout server.pem -out server.pem -days 365
當然,port number 最好是設在 443。
server.port = 443

不過,如果是要讓 80 port 和 443 port 同時使用,
可以改為以下:
server.port = 80
$SERVER["socket"]==":443" {
ssl.engine = "enable"
ssl.pemfile = "server.pem"

}

 



lighty的官方文件對SSL配置寫的已經比較詳細了,搞懂SSL簽名流程就可以了

SSL簽名流程如下

下載

apt-get install openssl*

配置原始碼時加上 ./configure --with-openssl --with-openssl-libs=/usr/lib
用 lighttpd -v ,可以看到(ssl)
lighttpd/1.4.28 (ssl) - a light and fast webserver
Build-Date: Dec 24 2010 17:19:54


生成包含私鑰和ca的檔案,自我認證。可以通過第三方鏈條驗證
openssl req -new -x509  -keyout server.pem -out server.pem  -days 365 -nodes

在conf檔案中至少進行如下配置,SSL的預設埠為443

$SERVER["socket"] == ":443" {
       ssl.engine   = "enable"
       ssl.pemfile  = "/home/jli/network/lighttpd-1.4.28/conf/server.pem"
}

在conf檔案中至少進行如下配置,SSL的預設埠為443

$SERVER["socket"] == ":443" {
       ssl.engine   = "enable"
       ssl.pemfile  = "/home/jli/network/lighttpd-1.4.28/conf/server.pem"
}




ssl.engine     enable/disable ssl engine
ssl.pemfile     path to the PEM file for SSL support 包含私鑰和ca認證檔案
ssl.ca-file     path to the CA file for support of chained certificates。
ssl.use-sslv2     enable/disable use of SSL version 2 (lighttpd < 1.4.21 only, newer version don't support SSLv2)
ssl.cipher-list     Configure the allowed SSL ciphers
ssl.verifyclient.activate     enable/disable client verification
ssl.verifyclient.enforce     enable/disable enforcing client verification
ssl.verifyclient.depth     certificate depth for client verification
ssl.verifyclient.exportcert     enable/disable client certificate export to env:SSL_CLIENT_CERT
ssl.verifyclient.username     client certificate entity to export as env:REMOTE_USER (eg. SSL_CLIENT_S_DN_emailAddress, SSL_CLIENT_S_DN_UID, etc.)




fastcgi.server             = ( ".php" =>
                               ( "localhost" =>
                                 (
                                   "socket" => "/tmp/php-fastcgi.socket",
                                   "bin-path" => "/usr/bin/php-cgi"
                                 )
                               )
                            )
server.modules = (
    "mod_access", 
    "mod_fastcgi", 
# "mod_alias", 
# "mod_auth", 
# "mod_evasive", 
# "mod_redirect", 
# "mod_rewrite", 
# "mod_setenv", 
# "mod_usertrack", 
)