yannstatic/static/2020/12/15/ArchLinux-KVM_QEMU-VM-debian10-vdb-statique.html

2548 lines
221 KiB
HTML
Raw Permalink Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>PC1 ArchLinux - VM Debian 10 vdb (jekyll-statique) - YannStatic</title>
<meta name="description" content="VM Debian 10">
<link rel="canonical" href="https://static.rnmkcy.eu/2020/12/15/ArchLinux-KVM_QEMU-VM-debian10-vdb-statique.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root"><!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main">
<div class="header__title">
<div class="header__brand"><svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewBox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.5
" href="/">YannStatic</a></div><!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">PC1 ArchLinux - VM Debian 10 vdb (jekyll-statique)</h1></header></div><meta itemprop="headline" content="PC1 ArchLinux - VM Debian 10 vdb (jekyll-statique)"><div class="article__info clearfix"><ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=virtuel">virtuel</a>
2024-10-31 20:18:37 +01:00
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">15&nbsp;déc.&nbsp;&nbsp;2020</span>
<span title="Modification" style="color:#00FF7F">&nbsp;2&nbsp;févr.&nbsp;2021</span></li></ul></div><meta itemprop="datePublished" content="2021-02-02T00:00:00+01:00">
<meta itemprop="keywords" content="virtuel"><div class="js-article-content">
<div class="layout--article"><!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page">&#8679;</button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody"><details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><h2 id="vm-debian-10">VM Debian 10</h2>
<p><img src="/images/debian-buster-logo1.png" alt="Debian Buster" width="100" /></p>
<h3 id="créer-machine-virtuelle-debian">Créer Machine Virtuelle Debian</h3>
<p>En mode su</p>
<p>Création réseau <strong>host-bridge</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh net-define host-bridge.xml
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;network&gt;
&lt;name&gt;host-bridge&lt;/name&gt;
&lt;forward mode='bridge'/&gt;
&lt;bridge name='br0'/&gt;
&lt;/network&gt;
</code></pre></div></div>
<p>Avant la création dune machine virtuelle, le réseau doit être actif</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh net-start host-bridge # virsh net-autostart host-bridge
</code></pre></div></div>
<p>Configurer le réseau pour un lancement au démarrage du serveur</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh net-autostart host-bridge # Network host-bridge marked as autostarted
</code></pre></div></div>
<p>Liste des réseaux déclarés</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh net-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique Persistent
-------------------------------------------------------------
default inactif no Oui
host-bridge actif Oui Oui
</code></pre></div></div>
<h3 id="création-vm-en-mode-console">Création VM en mode console</h3>
<p>Créer la machine virtuelle debian buster : <strong>debyan10</strong></p>
<p>Machine Virtuelle : debyan10<br />
Processeurs : 2<br />
Mémoire : 4096 Mo
Disque dur SATA : /home/yann/virtuel/KVM/debyan10.qcow2 15Go<br />
CDROM SATA : /home/yann/media/dplus/iso/debian/buster/debian-10.4.0-amd64-netinst.iso<br />
NIC Réseau : Réseau virtuel host-bridge</p>
<p>Installation en mode console , il faut aller chercher le lien debian sur le site <a href="http://ftp.fr.debian.org/debian/dists/">http://ftp.fr.debian.org/debian/dists/</a></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virt-install \
--name debyan10 \
--os-type Linux \
--os-variant debian10 \
--ram=4096 \
--disk /home/yann/virtuel/KVM/debyan10.qcow2,device=disk,bus=virtio,size=15,format=qcow2 \
--vcpus 2 \
--network bridge=br0 \
--location="http://ftp.fr.debian.org/debian/dists/buster/main/installer-amd64/" \
--extra-args="auto console=ttyS0,115200n8 serial" \
--graphics none \
--console pty,target_type=serial
</code></pre></div></div>
<p>Renseignements durant linstallation</p>
<p>Machine (hostname) : vdb<br />
root : ytreu49<br />
utilisateur: bust<br />
Mot de passe : bust49<br />
Disque entier, 1 seule partition<br />
Choix : “SSH Server” et “standard system utilities”</p>
<p>A la fin de linstallation ,reboot</p>
<p><img src="/images/vdb-tty001.png" alt="" /></p>
<p>Mofifier le réseau pour un adressage ip fixe : <code class="language-plaintext highlighter-rouge">nano /etc/network/interfaces</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug enp1s0
#iface enp1s0 inet dhcp
iface enp1s0 inet static
address 192.168.0.100
netmask 255.255.255.0
gateway 192.168.0.254
# This is an autoconfigured IPv6 interface
iface enp1s0 inet6 auto
</code></pre></div></div>
<p>On redémarre la machine virtuelle</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl reboot
</code></pre></div></div>
<p>Accès root via ssh</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh root@192.168.0.100
</code></pre></div></div>
<p>Réseau</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip a
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
2: enp1s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UP gro
up default qlen 1000
link/ether 52:54:00:98:a5:d7 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.100/24 brd 192.168.0.255 scope global enp1s0
valid_lft forever preferred_lft forever
inet6 2a01:e34:eebf:df0:5054:ff:fe98:a5d7/64 scope global dynamic mngtmpaddr
valid_lft 86358sec preferred_lft 86358sec
inet6 fe80::5054:ff:fe98:a5d7/64 scope link
valid_lft forever preferred_lft forever
</code></pre></div></div>
<p>Installer sudo : <code class="language-plaintext highlighter-rouge">apt install sudo</code> <br />
Les accès root utilisateur <strong>bust</strong> : <code class="language-plaintext highlighter-rouge">echo "bust ALL=(ALL) NOPASSWD: ALL" &gt;&gt; /etc/sudoers</code> <br />
Connexion SSH avec clés , ajout clé publique dans <code class="language-plaintext highlighter-rouge">mkdir ~/.ssh , ~/.ssh/authorized_keys</code> + droits <code class="language-plaintext highlighter-rouge">chmod 600 -R ~/.ssh</code><br />
Configuration <code class="language-plaintext highlighter-rouge">/etc/ssh/sshd_config</code> : <code class="language-plaintext highlighter-rouge">Port 55100</code> ,<code class="language-plaintext highlighter-rouge">PasswordAuthentication no</code> <br />
Redémarrer le service : <code class="language-plaintext highlighter-rouge">sudo systemctl restart sshd</code></p>
<p>Test liaison SSH :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh bust@192.168.0.100 -p 55100 -i /home/yann/.ssh/kvm-vdb-ed25519
</code></pre></div></div>
<p>Installer utilitaires : <code class="language-plaintext highlighter-rouge">sudo apt install rsync curl tmux jq figlet git dnsutils tree socat</code><br />
Bannière <code class="language-plaintext highlighter-rouge">/etc/motd</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> __ __ __ __ _ _ _ _ __
\ \ / /| \/ | __| | ___ | |__ (_) __ _ _ _ / | / \
\ V / | |\/| | / _` |/ -_)| '_ \| |/ _` || ' \ | || () |
_\_/__|_|__|_| \__,_|\___||_.__/|_|\__,_||_||_||_|_\__/
/ |/ _ \|_ ) / | / / ( _ ) / \ / | / \ / \
| |\_, / / / _ | |/ _ \/ _ \ _| () |_ | || () || () |
|_| /_/ /___|(_)|_|\___/\___/(_)\__/(_)|_| \__/ \__/
</code></pre></div></div>
<p>Bash <code class="language-plaintext highlighter-rouge">wget -4 https://static.xoyaz.xyz/files/ssh_rc_bash &amp;&amp; chmod +x ssh_rc_bash &amp;&amp; ./ssh_rc_bash</code></p>
<p><img src="/images/vdb01.png" alt="" /></p>
<p>Pour des raisons de conformité avec archlinux, modifier ID groupe “users”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo groupmod -g 985 users
</code></pre></div></div>
<h3 id="partage-xoyize">Partage xoyize</h3>
<p>Installer <strong>autofs</strong> pour un accès aux dossiers de la machine <em>xoyize.xyz</em> 192.168.0.46</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install autofs
</code></pre></div></div>
<p>Déclarer <em>xoyize</em> dans <code class="language-plaintext highlighter-rouge">/etc/hosts</code> , en ajoutant au fichier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>192.168.0.46 xoyize
</code></pre></div></div>
<p>Les partages disponibles</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo showmount -e xoyize
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Export list for xoyize:
/xoyipart 192.168.0.0/24
</code></pre></div></div>
<p>Déclaration des répertoires parents de montages et de leur types</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/auto.master
</code></pre></div></div>
<p>Oter le commentaire de la ligne suivante</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/net -hosts
</code></pre></div></div>
<p>Redémarrer le service</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart autofs
</code></pre></div></div>
<p>Le dossier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ls /net/xoyize/xoyipart
</code></pre></div></div>
<p>Créer un groupe <em>partage</em> avec id 9999</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo groupadd --gid 9999 partage
</code></pre></div></div>
<h3 id="sshfs---partage-statique">SSHFS - Partage “statique”</h3>
<p><img src="/images/sshfs-logo.png" alt="sshfs" width="50" /></p>
<p>Le dossier “statique” est sur lhôte PC1 (archyan)
Il faut créer une liaison réseau sécurisée entre <strong>vdb ← → archyan</strong><br />
On va utiliser SSHFS (<em>Secure shell file system (ou SSHFS) permet le partage dun système de fichiers de manière sécurisée en utilisant le protocole SFTP de SSH</em>)</p>
<p>Installation</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install sshfs
</code></pre></div></div>
<p>Autorisations</p>
<ul>
<li>Autorisations “utilisateur”
<ul>
<li>Exécuter <code class="language-plaintext highlighter-rouge">sshfs</code> (ou toute autre commande de montage FUSE) avec loption <code class="language-plaintext highlighter-rouge">-o allow_other</code></li>
</ul>
</li>
<li>Autoriser laccès “root” des supports <strong>fuse</strong>
<ul>
<li>Ajouter <code class="language-plaintext highlighter-rouge">user_allow_other</code> au fichier <strong>/etc/fuse.conf</strong></li>
<li>Exécuter <code class="language-plaintext highlighter-rouge">sshfs</code> (ou toute autre commande de montage FUSE) avec loption <code class="language-plaintext highlighter-rouge">-o allow_root</code></li>
</ul>
</li>
</ul>
<blockquote>
<p>PC1 : Clé publique pc1-archyan.pub ajoutée au fichier authorized_keys : <code class="language-plaintext highlighter-rouge">cat .ssh/pc1-archyan.pub &gt;&gt; .ssh/authorized_keys</code></p>
</blockquote>
<p>Clé privée <strong>pc1-archyan</strong> pour accéder à PC1 archyan (192.168.1.43)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano $HOME/.ssh/pc1-archyan # copier la clé privée
chmod 600 $HOME/.ssh/pc1-archyan
</code></pre></div></div>
<p><strong>Exécution manuelle</strong> pour authentifier la clé avec utilisateur “bust”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p $HOME/statique
sshfs -oIdentityFile=/home/bust/.ssh/pc1-archyan yann@192.168.0.43:/home/yann/media/statique /home/bust/statique
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The authenticity of host <span class="s1">'[192.168.1.43]:55036 ([2a04:52c0:101:82::73db]:55036)'</span> can<span class="s1">'t be established.
ECDSA key fingerprint is SHA256:NuFqR5id10fVzRLsSTqJ4vBpFnNYi+APGsvPYth6PHw.
Are you sure you want to continue connecting (yes/no)? yes
</span></code></pre></div></div>
<blockquote>
<p>NOTE: Il faut mettre ladresse IP du serveur , si les domaines peuvent ne pas être “résolus”</p>
</blockquote>
<p>Après vérification , <code class="language-plaintext highlighter-rouge">ls $HOME/statique</code> , déconnexion <code class="language-plaintext highlighter-rouge">fusermount -u $HOME/statique</code></p>
<h4 id="montage-fstab">Montage fstab</h4>
<p>Le générateur de site statique utilise les dossiers _posts, images et files présents sur lordinateur hôte PC1<br />
On va créer montage de chaque dossier sur la machine virtuelle debian</p>
<p>ajouter les lignes suivantes au fichier <code class="language-plaintext highlighter-rouge">/etc/fstab</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yann@192.168.0.43:/home/yann/media/statique/_posts /home/bust/yannstatic/_posts fuse.sshfs _netdev,identityfile<span class="o">=</span>/home/bust/.ssh/pc1-archyan,allow_other,port<span class="o">=</span>22 0 0
yann@192.168.0.43:/home/yann/media/statique/files /home/bust/yannstatic/files fuse.sshfs _netdev,identityfile<span class="o">=</span>/home/bust/.ssh/pc1-archyan,allow_other,port<span class="o">=</span>22 0 0
yann@192.168.0.43:/home/yann/media/statique/images /home/bust/yannstatic/images fuse.sshfs _netdev,identityfile<span class="o">=</span>/home/bust/.ssh/pc1-archyan,allow_other,port<span class="o">=</span>22 0 0
</code></pre></div></div>
<p>Montage pour authentifier la clé avec utilisateur “root”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mount -a
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The authenticity of host '192.168.0.43 (192.168.0.43)' can't be established.
ECDSA key fingerprint is SHA256:BxrhdNWnJS6934RvGgZs8XxGkUnlkIWu8KgU2goxnB0.
Are you sure you want to continue connecting (yes/no)? yes
</code></pre></div></div>
<p>Vérification</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ls $HOME/yannstatic/{images,files,_posts}
</code></pre></div></div>
<h3 id="sortie-du-mode-console">Sortie du mode console</h3>
<p>La combinaison <code class="language-plaintext highlighter-rouge">Ctrl + ]</code> ne focntionne pas<br />
On recherche le PID de la console pour la supprimer</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux | grep tty
</code></pre></div></div>
<p><img src="/images/vdb-tty002.png" alt="" /></p>
<p>Correspondance avec le pid 1419 , supprimer le processus : <code class="language-plaintext highlighter-rouge">sudo kill 1419</code></p>
<h3 id="vm-autostart">VM autostart</h3>
<p><strong>Sur lhôte PC1</strong><br />
Démarrer la machine virtuelle au boot</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh autostart debyan10
</code></pre></div></div>
<p><em>Domaine debyan10 marqué pour le démarrage automatique</em></p>
<h2 id="jekyll-générateur-site-statique">Jekyll générateur site statique</h2>
<p><img src="/images/jekyll-300x133.png" alt="jekyll" width="150px" /><br />
<em>Générer un site statique via jekyll</em></p>
<p><strong>Rbenv (gestionnaire version ruby)</strong> est un outil léger de gestion des versions de Ruby qui vous permet de changer facilement de version de Ruby.</p>
<p>Par défaut, Rbenv ne gère pas linstallation des versions de Ruby. ruby-build est un outil qui vous aide à installer nimporte quelle version de Ruby dont vous pourriez avoir besoin. Il est disponible en tant que programme autonome et en tant que plugin pour rbenv.</p>
<p>Installez les dépendances nécessaires à loutil ruby-build pour construire Ruby à partir des sources :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt update
sudo apt install git curl libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential libyaml-dev libreadline-dev libncurses5-dev libffi-dev libgdbm-dev
</code></pre></div></div>
<p>Ensuite, lancez la commande curl suivante pour installer les scripts rbenv et ruby-build :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl -sL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash -
</code></pre></div></div>
<p>Si linstallation est réussie, le script imprimera quelque chose comme ceci :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Running doctor script to verify installation...
Checking for `rbenv' in PATH: not found
You seem to have rbenv installed in `/home/dbsuser/.rbenv/bin', but that
directory is not present in PATH. Please add it to PATH by configuring
your `~/.bashrc', `~/.zshrc', or `~/.config/fish/config.fish'.
</code></pre></div></div>
<p>Avant de commencer à utiliser rbenv, nous devons ajouter $HOME/.rbenv/bin à notre PATH.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo 'export PATH="$HOME/.rbenv/bin:$PATH"' &gt;&gt; ~/.bashrc
echo 'eval "$(rbenv init -)"' &gt;&gt; ~/.bashrc
source ~/.bashrc
</code></pre></div></div>
<p>Les dernières versions stables</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rbenv install -l
</code></pre></div></div>
<p>Maintenant que rbenv est installé sur notre système, nous pouvons facilement installer la dernière version stable de Ruby et la définir comme version par défaut avec :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rbenv install 2.7.2 # patientez de 3 à 8 minutes
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Downloading ruby-2.7.2.tar.bz2...
-&gt; https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.bz2
Installing ruby-2.7.2...
Installed ruby-2.7.2 to /home/xoyi/.rbenv/versions/2.7.2
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rbenv global 2.7.2
</code></pre></div></div>
<p>Vérifiez que Ruby a été correctement installé en imprimant le numéro de version :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby -v
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]</code></p>
<p>Ajout du chemin bin $HOME/.rbenv/versions/2.7.2/bin/</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo 'export PATH="$HOME/.rbenv/versions/2.7.2/bin:$PATH"' &gt;&gt; ~/.bashrc
source ~/.bashrc
</code></pre></div></div>
<h3 id="yannstatic">yannstatic</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd $HOME
</code></pre></div></div>
<p>Cloner le dépôt</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~
git clone https://gitea.cinay.eu/yann/yannstatic.git yannstatic
cd yannstatic
</code></pre></div></div>
<p>Bundle lit le fichier <strong>Gemfile</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle install # Patientez ...
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bundle complete! 3 Gemfile dependencies, 43 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
</code></pre></div></div>
<p>Info sur jekyll et créer un lien</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle info jekyll
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> * jekyll (4.1.1)
Summary: A simple, blog aware, static site generator.
Homepage: https://jekyllrb.com
Path: /home/bust/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/jekyll-4.1.1
</code></pre></div></div>
<p><strong>Génération dossier _site Manuellement</strong><br />
Création du dossier <strong>_site</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll build
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Configuration file: /home/bust/yannstatic/_config.yml
Source: /home/bust/yannstatic
Destination: /home/bust/yannstatic/_site
Incremental build: disabled. Enable with --incremental
Generating...
Jekyll Feed: Generating feed for posts
done in 49.398 seconds.
Auto-regeneration: disabled. Use --watch to enable.
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">yannstatic.service</code>** - Service yannstatic, génération _site en cas de modification**<br />
Pour lancer le serveur <strong>yannstatic</strong> au démarrage, utilisation dun <u>service systemd</u><br />
<strong>ATTENTION!</strong> , remplacer <em>User=utilisateur</em> par votre nom dutilisateur <code class="language-plaintext highlighter-rouge">echo $USER</code><br />
Relever le chemin complet de jekyll : <code class="language-plaintext highlighter-rouge">which jekyll</code><strong>/home/bust/.rbenv/shims/jekyll</strong></p>
<p>Création dun service “yannstatic” sous systemd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/systemd/system/yannstatic.service
</code></pre></div></div>
<p>Contenu du fichier</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">service yannstatic</span>
<span class="py">After</span><span class="p">=</span><span class="s">network.target</span>
<span class="nn">[Service]</span>
<span class="py">Type</span><span class="p">=</span><span class="s">simple</span>
<span class="py">User</span><span class="p">=</span><span class="s">bust</span>
<span class="py">WorkingDirectory</span><span class="p">=</span><span class="s">/home/bust/yannstatic</span>
<span class="py">ExecStart</span><span class="p">=</span><span class="s">/home/bust/.rbenv/shims/bundle exec jekyll build --watch</span>
<span class="py">Restart</span><span class="p">=</span><span class="s">on-abort</span>
<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span>
</code></pre></div></div>
<p>Activer et lancer le service <strong>yannstatic</strong> :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl daemon-reload
sudo systemctl enable yannstatic
sudo systemctl start yannstatic
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">synchro_site.{path,service}</code>** - En cas de modification du dossier _site, synchronisation locale → distante xoyaz.xyz**<br />
<em>Avec les unités de chemin, vous pouvez surveiller les fichiers et les répertoires pour certains événements. Si un événement spécifique se produit, une unité de service est exécutée, et elle porte généralement le même nom que lunité de chemin.</em></p>
<p>Nous allons surveiller le fichier <strong>sitemap.xml</strong> du dossier <em>_site/</em>. Chaque fois que le fichier est fermé après une écriture, un script spécifique démarrera.</p>
<p>Dans le répertoire <code class="language-plaintext highlighter-rouge">/etc/systemd/system/</code> nous créons une unité de cheminement <strong>synchro_site.path</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/systemd/system/synchro_site.path
</code></pre></div></div>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">Surveiller le fichier pour les changements</span>
<span class="nn">[Path]</span>
<span class="py">PathChanged</span><span class="p">=</span><span class="s">/home/bust/yannstatic/_site/sitemap.xml</span>
<span class="py">Unit</span><span class="p">=</span><span class="s">synchro_site.service</span>
<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span>
</code></pre></div></div>
<p>Dans la section <code class="language-plaintext highlighter-rouge">[Path]</code>, <code class="language-plaintext highlighter-rouge">PathChanged=</code> indique le chemin absolu du fichier à surveiller, tandis que <code class="language-plaintext highlighter-rouge">Unit=</code> indique lunité de service à exécuter si le fichier change. Cette unité (<strong>synchro_site.path</strong>) doit être lancée lorsque le système est en mode multi-utilisateur.</p>
<p>Ensuite, nous créons lunité de service correspondante, <strong>synchro_site.service</strong>, dans le répertoire <code class="language-plaintext highlighter-rouge">/etc/systemd/system/</code> <br />
Si le fichier <strong>sitemap.xml</strong> change (cest-à-dire quil est à la fois écrit et fermé), lunité de service suivante sera appelée pour exécuter le script spécifié :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/systemd/system/synchro_site.service
</code></pre></div></div>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">"/home/bust/yannstatic/_site/sitemap.xml a été modifié."</span>
<span class="nn">[Service]</span>
<span class="py">ExecStart</span><span class="p">=</span><span class="s">/home/bust/yannstatic/synchro_site.sh</span>
<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span>
</code></pre></div></div>
<p>Le script <strong>/home/bust/yannstatic/synchro_site.sh</strong> lance une synchronisation locale distante via rsync ssh</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Synchronisation locale distante du dossier _site</span>
rsync <span class="nt">-avz</span> <span class="nt">--progress</span> <span class="nt">--stats</span> <span class="nt">--human-readable</span> <span class="nt">--delete</span> <span class="nt">--rsync-path</span><span class="o">=</span><span class="s2">"sudo rsync"</span> <span class="nt">-e</span> <span class="s2">"ssh -p 55036 -i /home/bust/.ssh/OVZ-STORAGE-128 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"</span> /home/bust/yannstatic/_site/<span class="k">*</span> usernl@xoyaz.xyz:/home/usernl/backup/static/ <span class="o">&gt;</span> /dev/null
<span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> <span class="nt">-eq</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\</span>
<span class="nb">echo</span> <span class="s2">"Synchronisation dossier _site entre vdb et xoyayz -&gt; OK"</span> | systemd-cat <span class="nt">-t</span> vdbsync <span class="nt">-p</span> info <span class="p">;</span> <span class="se">\</span>
<span class="k">else</span> <span class="se">\</span>
<span class="nb">echo</span> <span class="s2">"Synchronisation dossier _site entre vdb et xoyayz -&gt; ERREUR"</span> | systemd-cat <span class="nt">-t</span> vdbsync <span class="nt">-p</span> emerg <span class="p">;</span> <span class="se">\</span>
<span class="k">fi</span>
</code></pre></div></div>
<p><strong>Activation des services</strong><br />
Activer et lancer le service <strong>yannstatic</strong> :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl daemon-reload
sudo systemctl enable yannstatic
sudo systemctl start yannstatic
</code></pre></div></div>
<p>Les unités <code class="language-plaintext highlighter-rouge">synchro_site.{path,service}</code> doivent être activées</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl daemon-reload
sudo systemctl enable synchro_site.{path,service}
sudo systemctl start synchro_site.path
</code></pre></div></div>
<p>Vérifications</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo journalctl -f -u yannstatic.service
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nov. 29 14:02:31 vdb jekyll[2139]: Regenerating: 2 file(s) changed at 2020-11-29 14:02:31
nov. 29 14:02:31 vdb jekyll[2139]: _posts/2020-11-27-ArchLinux-KVM_QEMU-VM-debian10-vdb-statique.md
nov. 29 14:02:31 vdb jekyll[2139]: _posts/.2020-11-27-ArchLinux-KVM_QEMU-VM-debian10-vdb-statique.md.LOyEEU
nov. 29 14:02:31 vdb jekyll[2139]: Jekyll Feed: Generating feed for posts
nov. 29 14:02:45 vdb jekyll[2139]: ...done in 14.660143584 seconds.
nov. 29 14:02:45 vdb jekyll[2139]:
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo journalctl -f -u synchro_site.service
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nov. 29 14:02:45 vdb systemd[1]: Started "/home/bust/yannstatic/_site/sitemap.xml a été modifié.".
nov. 29 14:02:45 vdb synchro_site.sh[2635]: Warning: Permanently added '[xoyaz.xyz]:55036,[2a04:52c0:101:82::73db]:55036' (ECDSA) to the list of known hosts.
nov. 29 14:02:47 vdb systemd[1]: synchro_site.service: Succeeded.
</code></pre></div></div>
<p><strong>VM - Dossiers _posts, images et files</strong> <br />
On ne peut pas traiter la modification de dossier avec systemd.path dans la machine virtuelle car les dossiers sont “liés” (ln).
Créer dossiers <code class="language-plaintext highlighter-rouge">/home/bust/yannstatic/{_posts,images,files}</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p /home/bust/yannstatic/{_posts,images,files}
</code></pre></div></div>
<h3 id="pc1---synchroniser-les-dossiers-_posts-images-et-files">PC1 - Synchroniser les dossiers _posts, images et files</h3>
<p>en cas de modification, il faut mettre à jour les dossiers _posts, images et files de la VM par rsync avec le script <code class="language-plaintext highlighter-rouge">/home/yann/scripts/synchro-dossiers_posts-images-files_PC1_vers_vdb</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c">#!/bin/bash </span>
<span class="c"># COLUMNSxROWS+X+Y -&gt; xterm -geometry 400x200+100+350</span>
<span class="c"># xterm -geometry 100x30+100+350 -T yannstatic -e '/home/yann/scripts/tmux-vdb.sh'</span>
<span class="c"># On supprime la session "VdbSess" si existante </span>
<span class="k">if</span> <span class="o">[[</span> <span class="sb">`</span>tmux list-sessions | <span class="nb">grep</span> <span class="s1">'VdbSess'</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="sb">`</span> <span class="o">!=</span> 0 <span class="o">]]</span>
<span class="k">then</span>
<span class="c"># On ferme la session active</span>
tmux kill-session <span class="nt">-t</span> VdbSess
<span class="k">fi</span>
<span class="c"># Créer une bannière dans /etc/motd sur chaque serveur</span>
<span class="c"># Nom du domaine en ascii voir lien http://patorjk.com/software/taag/#p=display&amp;h=1&amp;f=Small</span>
<span class="nb">echo</span> <span class="s2">"fin fi"</span>
<span class="c"># Créer session + terminal </span>
tmux new <span class="nt">-d</span> <span class="nt">-s</span> VdbSess <span class="nt">-n</span> VdbTerm
<span class="c"># Séparation horizontale en 2/3 1/3 du terminal</span>
tmux split-window <span class="nt">-v</span> <span class="nt">-p</span> 33
<span class="c"># | 0 |</span>
<span class="c"># | 2/3 |</span>
<span class="c"># | |</span>
<span class="c"># |-----------|</span>
<span class="c"># | 1 1/3 |</span>
<span class="c"># Sélection terminal HAUT</span>
tmux selectp <span class="nt">-t</span> 0
<span class="c"># Séparation horizontale en 1/2 du terminal 0</span>
tmux split-window <span class="nt">-v</span> <span class="nt">-p</span> 50
<span class="c"># Sélection terminal HAUT</span>
tmux selectp <span class="nt">-t</span> 0
tmux send-keys <span class="nt">-t</span> VdbSess <span class="s2">"sshm vdb exe 'sudo journalctl -f -u yannstatic.service'"</span> C-m
<span class="c"># Sélection terminal MILIEU</span>
tmux selectp <span class="nt">-t</span> 1
<span class="c">#tmux send-keys -t VdbSess "sudo journalctl -f -u rsync_statique.service" C-m</span>
tmux send-keys <span class="nt">-t</span> VdbSess <span class="s2">"sudo journalctl -f -t vdbsync --since today"</span> C-m
tmux selectp <span class="nt">-t</span> 2
<span class="c">#tmux send-keys -t VdbSess "sshm vdb exe 'sudo journalctl -f -u synchro_site.service'" C-m</span>
tmux send-keys <span class="nt">-t</span> VdbSess <span class="s2">"sshm vdb exe 'sudo journalctl -f -t vdbsync --since today'"</span> C-m
<span class="c"># Sélection terminal BAS</span>
<span class="c"># Afficher la session </span>
tmux a <span class="nt">-t</span> VdbSess
<span class="c">#</span>
<span class="c"># Tuer la session</span>
<span class="c"># tmux kill-session -t VdbSess</span>
</code></pre></div></div>
<p>Dans la section <code class="language-plaintext highlighter-rouge">[Path]</code>, <code class="language-plaintext highlighter-rouge">PathModified=</code> indique le chemin absolu du fichier à surveiller,
<code class="language-plaintext highlighter-rouge">PathModified=</code> est utilisé pour surveiller un fichier ou un répertoire et activer lunité configurée chaque fois quelle change. Il activé lors de simples écritures sur le fichier/dossier surveillé.</p>
<p>tandis que <code class="language-plaintext highlighter-rouge">Unit=</code> indique lunité de service à exécuter si le fichier change. Cette unité (<strong>rsync_statique.path</strong>) doit être lancée lorsque le système est en mode multi-utilisateur.</p>
<p>Ensuite, nous créons lunité de service correspondante, <strong>rsync_statique.service</strong>, dans le répertoire <code class="language-plaintext highlighter-rouge">/etc/systemd/system/</code> <br />
Si le dossier <strong>/home/yann/media/statique/_posts</strong> est modifié , lunité de service <strong>rsync_statique.service</strong> sera appelé pour exécuter le script spécifié :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/systemd/system/rsync_statique.service
</code></pre></div></div>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">"/home/yann/media/statique/_posts a été modifié"</span>
<span class="nn">[Service]</span>
<span class="py">ExecStart</span><span class="p">=</span><span class="s">/home/yann/scripts/synchro-dossiers_posts-images-files_PC1_vers_vdb</span>
<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span>
</code></pre></div></div>
<p>Les unités <code class="language-plaintext highlighter-rouge">rsync_statique.{path,service}</code> doivent être activées</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl daemon-reload
sudo systemctl enable rsync_statique.{path,service}
sudo systemctl start rsync_statique.path
</code></pre></div></div>
<p>Vérification</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo journalctl -f -u rsync_statique.service
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-- Logs begin at Mon 2020-11-23 16:54:55 CET. --
nov. 29 14:17:01 archyan synchro-dossiers_posts-images-files_PC1_vers_vdb[15264]: Warning: Permanently added '[192.168.0.100]:55100' (ECDSA) to the list of known hosts.
nov. 29 14:17:01 archyan systemd[1]: rsync_statique.service: Succeeded.
</code></pre></div></div>
<h3 id="surveillance-depuis-lhôte-pc1">Surveillance depuis lhôte (PC1)</h3>
<p>Créer un script <code class="language-plaintext highlighter-rouge">/home/yann/scripts/tmux-vdb.sh</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash </span>
<span class="c"># COLUMNSxROWS+X+Y -&gt; xterm -geometry 400x200+100+350</span>
<span class="c"># xterm -geometry 100x30+100+350 -T yannstatic -e '/home/yann/scripts/tmux-vdb.sh'</span>
<span class="c"># On supprime la session "VdbSess" si existante </span>
<span class="k">if</span> <span class="o">[[</span> <span class="sb">`</span>tmux list-sessions | <span class="nb">grep</span> <span class="s1">'VdbSess'</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="sb">`</span> <span class="o">!=</span> 0 <span class="o">]]</span>
<span class="k">then</span>
<span class="c"># On ferme la session active</span>
tmux kill-session <span class="nt">-t</span> VdbSess
<span class="k">fi</span>
<span class="c"># Créer une bannière dans /etc/motd sur chaque serveur</span>
<span class="c"># Nom du domaine en ascii voir lien http://patorjk.com/software/taag/#p=display&amp;h=1&amp;f=Small</span>
<span class="nb">echo</span> <span class="s2">"fin fi"</span>
<span class="c"># Créer session + terminal </span>
tmux new <span class="nt">-d</span> <span class="nt">-s</span> VdbSess <span class="nt">-n</span> VdbTerm
<span class="c"># Séparation horizontale en 2 du terminal</span>
tmux split-window <span class="nt">-v</span> <span class="nt">-p</span> 50
<span class="c"># | 0 |</span>
<span class="c"># | |</span>
<span class="c"># |-----------|</span>
<span class="c"># | 1 |</span>
<span class="c"># | |</span>
<span class="c"># Sélection terminal HAUT</span>
tmux selectp <span class="nt">-t</span> 0
<span class="c"># </span>
tmux send-keys <span class="nt">-t</span> VdbSess <span class="s2">"sshm vdb exe 'sudo journalctl -f -u yannstatic.service'"</span> C-m
<span class="c"># Sélection terminal BAS</span>
tmux selectp <span class="nt">-t</span> 1
<span class="c"># </span>
tmux send-keys <span class="nt">-t</span> VdbSess <span class="s2">"sshm vdb exe 'sudo journalctl -f -u synchro_site.service'"</span> C-m
<span class="c"># Afficher la session </span>
tmux a <span class="nt">-t</span> VdbSess
<span class="c">#</span>
<span class="c"># Tuer la session</span>
<span class="c"># tmux kill-session -t VdbSess</span>
</code></pre></div></div>
<p>Installer <strong>xterm</strong> : <code class="language-plaintext highlighter-rouge">sudo pacman -S xterm</code><br />
Créer un alias <strong>tmuxvdb</strong> pour visualiser la construction et la synchro du site statique</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano ~/.bashrc
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">tmuxvdb</span><span class="o">=</span><span class="s2">"xterm -geometry 100x30+100+350 -T yannstatic -e '/home/yann/scripts/tmux-vdb.sh'"</span>
</code></pre></div></div>
<p>Voir : <code class="language-plaintext highlighter-rouge">tmuxvdb</code></p>
<p><img src="/images/xterm01.png" alt="" width="500" /></p>
<h3 id="desactivation-site-statique">DESACTIVATION site statique</h3>
<p>Pour rendre inactif le générateur de site statique</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl stop yannstatic
sudo systemctl disable yannstatic
sudo systemctl stop synchro_site.path
sudo systemctl disable synchro_site.path
sudo systemctl stop synchro_site.service
sudo systemctl disable synchro_site.service
</code></pre></div></div>
<h2 id="nfs-xoyipart">NFS xoyipart</h2>
<p>Installer client NFS</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install nfs-common
</code></pre></div></div>
<p>Montage dossier <code class="language-plaintext highlighter-rouge">xoyipart</code> du serveur xoyize.xyz 192.168.0.46</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir $HOME/xoyipart
</code></pre></div></div>
<p>partage NFS avec les membres dun groupe</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo groupadd -g 9999 partage
sudo usermod -a -G partage $USER
</code></pre></div></div>
<p>Montage dans fichier <code class="language-plaintext highlighter-rouge">/etc/fstab</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>192.168.0.46:/xoyipart /home/bust/xoyipart nfs _netdev,nodev,noexec 0 0
</code></pre></div></div>
<p>Validation</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mount -a
</code></pre></div></div>
<h2 id="construire-navidrome">Construire Navidrome</h2>
<p><a href="https://www.navidrome.org/docs/installation/build-from-source/">https://www.navidrome.org/docs/installation/build-from-source/</a><br />
<em>Création dun binaire navidrome issu dune compilation en language go</em></p>
<p><a href="/2020/04/26/go.html">Installer Go version 15</a> <br />
Vérifier les chemins ajoutés au fichier <code class="language-plaintext highlighter-rouge">~/.bashrc</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export PATH=$PATH:/usr/local/go/bin
export GOPATH="$HOME/go_projects"
export GOBIN="$GOPATH/bin"
</code></pre></div></div>
<p>Pour actualiser : <code class="language-plaintext highlighter-rouge">source ~/.bashrc</code> si vous avez modifié le fichier</p>
<p>Copier un binaire</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo cp $HOME/go_projects/bin/go-bindata /usr/local/go/bin/
</code></pre></div></div>
<p><a href="/2019/12/28/Archlinux-Debian-Node.js-Nvm-Npm-Yarn.html">Installer nodejs version 14</a><br />
Dépendances :<code class="language-plaintext highlighter-rouge">sudo apt install libtag1-dev build-essential pkg-config</code> <br />
Cloner le projet : <code class="language-plaintext highlighter-rouge">git clone https://github.com/deluan/navidrome</code></p>
<ol>
<li>Aller dans le dossier : <code class="language-plaintext highlighter-rouge">cd navidrome/</code></li>
<li>Installer les dépendances de construction : <code class="language-plaintext highlighter-rouge">make setup</code></li>
<li>pour la langue :
<ol>
<li>ajouter le fichier <code class="language-plaintext highlighter-rouge">ui/src/i18n/fr.json</code></li>
<li>modifier provider.js <code class="language-plaintext highlighter-rouge">sed -i "s#'./en.json'#'./fr.json'#g" ui/src/i18n/provider.js</code></li>
</ol>
</li>
<li>Construire : <code class="language-plaintext highlighter-rouge">make buildall</code></li>
<li>Si OK, le fichier exécutable <code class="language-plaintext highlighter-rouge">navidrome</code> est généré</li>
<li>Il est nécessaire dinstaller le paquet libtag1-dev : <code class="language-plaintext highlighter-rouge">sudo apt install libtag1-dev</code></li>
</ol>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2020-12-15T00:00:00+01:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2020/11/29/Linux-Systemd-cheatsheet.html">Linux - Systemd cheatsheet</a></div><div class="next"><span>SUIVANT</span><a href="/2020/12/24/Archlinux_conteneur_LXC_debian_10_(buster).html">Archlinux --> Container LXC debian buster lxcdeb (go, node, jekyll, nginx, php, mariadb)</a></div></div></div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div></div>
</div><script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div></div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>