<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Шлакоблог</title><link>https://vodolaz095.ru/</link><description>Recent content on Шлакоблог</description><generator>Hugo -- 0.162.1</generator><language>ru-RU</language><lastBuildDate>Sat, 20 Jun 2026 13:01:51 +0300</lastBuildDate><atom:link href="https://vodolaz095.ru/index.xml" rel="self" type="application/rss+xml"/><item><title>OpenLDAP - как хранить несколько адресов почты для пользователя?</title><link>https://vodolaz095.ru/ldap_multiple_email/</link><pubDate>Sat, 20 Jun 2026 13:01:51 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/ldap_multiple_email/</guid><description>Как хранить несколько адресов почты для пользователя в LDAP</description><content type="text/html" mode="escaped"><![CDATA[<p>Допустим, в коллекции пользователей в Organization Unit <code>ou=people,dc=vodolaz095,dc=ru</code> у нас есть пользователь
с такими параметрами.</p>
<pre tabindex="0"><code>dn: uid=user3,ou=people,dc=vodolaz095,dc=ru
uid: user3
uidNumber: 1003
gidNumber: 1003
cn: Test User 3
givenName: Test User 3
homeDirectory: /home/user3
initials: AB
loginShell: /bin/bash
objectClass: posixAccount
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
sn: User3
title: testUser
userPassword: {SSHA}M1Um0cvQBqIRDjHk8beDv07kky5jQ2VB
mail: user3@vodolaz095.ru
</code></pre><p>И надо к нему добавить дополнительный, допустим, рабочий адрес электронной почты - <code>user3_work@vodolaz095.ru</code>.</p>
<h1 id="как-это-можно-сделать">Как это можно сделать?<a href="#%d0%ba%d0%b0%d0%ba-%d1%8d%d1%82%d0%be-%d0%bc%d0%be%d0%b6%d0%bd%d0%be-%d1%81%d0%b4%d0%b5%d0%bb%d0%b0%d1%82%d1%8c" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Сделать это очень просто — в схеме inetOrgPerson, которая применена к нашей учетной записи, атрибут <code>mail</code> по умолчанию
является многозначным (multi-valued). Это означает, что вы можете добавить сколько угодно адресов, просто указав
несколько строк mail: в LDIF-файле или выполнив операцию добавления (add), а не замены (replace).</p>
<h1 id="способ-1">Способ 1<a href="#%d1%81%d0%bf%d0%be%d1%81%d0%be%d0%b1-1" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Надо создать файл патча для записи - <code>add_email_address.ldif</code></p>
<pre tabindex="0"><code>dn: uid=user3,ou=people,dc=vodolaz095,dc=ru
changetype: modify
add: mail
mail: user3_work@vodolaz095.ru
</code></pre><p>C помощью утилиты <code>ldapmodify</code> запись можно обновить так</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ ldapmodify -x -D <span style="color:#e6db74">&#34;cn=admin,dc=vodolaz095,dc=ru&#34;</span> -W -f add_email_address.ldif
</span></span></code></pre></div><h1 id="способ-2">Способ 2<a href="#%d1%81%d0%bf%d0%be%d1%81%d0%be%d0%b1-2" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Также можно перезаписать ВСЕ адреса почты у пользователя - создадим файл <code>replace_email.ldif</code>:</p>
<pre tabindex="0"><code>dn: uid=user3,ou=people,dc=vodolaz095,dc=ru
changetype: modify
replace: mail
mail: user3_work@vodolaz095.ru
mail: user3@vodolaz095.ru
</code></pre><p>И применим его:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ ldapmodify -x -D <span style="color:#e6db74">&#34;cn=admin,dc=vodolaz095,dc=ru&#34;</span> -W -f replace_email.ldif
</span></span></code></pre></div><p>Как итог, у пользователя теперь появилось несколько адресов электронной почты:</p>
<pre tabindex="0"><code>dn: uid=user3,ou=people,dc=vodolaz095,dc=ru
uid: user3
uidNumber: 1003
gidNumber: 1003
cn: Test User 3
givenName: Test User 3
homeDirectory: /home/user3
initials: AB
loginShell: /bin/bash
objectClass: posixAccount
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
sn: User3
title: testUser
userPassword: {SSHA}M1Um0cvQBqIRDjHk8beDv07kky5jQ2VB
mail: user3@vodolaz095.ru
mail: user3_bis@vodolaz095.ru
</code></pre><h1 id="способ-3">Способ 3.<a href="#%d1%81%d0%bf%d0%be%d1%81%d0%be%d0%b1-3" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Можно использовать утилиту <a href="http://www.lichteblau.com/ldapvi/">ldapvi</a> и отредактировать всю коллекцию пользователей</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ ldapvi --discover --user<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;cn=admin,dc=vodolaz095,dc=ru&#34;</span>
</span></span></code></pre></div><p>После редактирования записей с помощью вашего любимого текстового редактора, можно сохранить временный файл, и ldapvi
спросит, что делать:</p>
<pre tabindex="0"><code>Action? [yYqQvVebB*rsf+?] ?
Commands:
  y -- commit changes
  Y -- commit, ignoring all errors
  q -- save changes as LDIF and quit
  Q -- discard changes and quit
  v -- view changes as LDIF change records
  V -- view changes as ldapvi change records
  e -- open editor again
  b -- show login dialog and rebind
  B -- toggle SASL
  * -- set SASL mechanism
  r -- reconnect to server
  s -- skip one entry
  f -- forget deletions
  + -- rewrite file to include schema comments
  ? -- this help
</code></pre><p>Обычно хватает <code>y</code>.</p>
]]></content></item><item><title>Контакты</title><link>https://vodolaz095.ru/contacts/</link><pubDate>Tue, 16 Jun 2026 21:56:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/contacts/</guid><description>&amp;lt;no value&amp;gt;</description><content type="text/html" mode="escaped"><![CDATA[<p>
    По всем вопросам просьба писать на этот адрес электронной почты:
    <a id="mail" href="mailto:report@spamhaus.org">report@spamhaus.org</a>.
</p>
<noscript>Пожалуйста, включите Javascript на этой странице! Вы можете многое потерять :-)</noscript>
<p>
  Мой профиль на гитхабе: <a target="_blank" href="https://github.com/vodolaz095/">github.com/vodolaz095</a>.
</p>
<p>
  Мой профиль в контакте: <a target="_blank" href="https://vk.com/vodolaz095">https://vk.com/vodolaz095</a>.
</p>
<p>
  Мой профиль в Telegram: <a target="_blank" href="https://t.me/@AnatolijSharpMinded">@AnatolijSharpMinded</a>.
</p>
<p>
  Мой профиль в Gitflic: <a target="_blank" href="https://gitflic.ru/vodolaz095/">gitflic.ru/vodolaz095</a>.
</p>
<p>
  Других социальных сетей у меня нет. Если Вам кажется, что Вы нашли меня где-то ещё, то это, скорее всего, самозванцы.
</p>
<script type="application/javascript">
    document.onreadystatechange = function (){
        const l = window.document.getElementById('mail');
        let str = String.fromCharCode(98,108,111,103,64);
        str = str + window.location.hostname;
        l.setAttribute("href", 'ma'+'il'+`to:${str}`);
        l.innerText = str;
    }
</script>
]]></content></item><item><title>Резюме</title><link>https://vodolaz095.ru/resume/</link><pubDate>Tue, 16 Jun 2026 21:56:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/resume/</guid><description>&amp;lt;no value&amp;gt;</description><enclosure url="https://vodolaz095.ru/images/vodolaz095.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/vodolaz095.jpg" width="800" height="1067"/><media:content url="https://vodolaz095.ru/images/vodolaz095.jpg" medium="image" type="image/jpeg" width="800" height="1067"><media:title type="html">/images/vodolaz095.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/vodolaz095.jpg" alt="vodolaz095 - как живой"></p>
<p>Люблю чай, мёд, мясо и программировать на golang и javascript.</p>
]]></content></item><item><title>Генератор QR кодов</title><link>https://vodolaz095.ru/qrcode/</link><pubDate>Fri, 09 Jan 2026 00:13:42 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/qrcode/</guid><description>&amp;lt;no value&amp;gt;</description><content type="text/html" mode="escaped"><![CDATA[<p><a href="https://ru.wikipedia.org/wiki/QR-%D0%BA%D0%BE%D0%B4">QR код</a> позволяет закодировать в абстрактном изображении
почти любой текст. Большинство смартфонов могут декодировать и прочитать QR код с помощью камеры.
Например, QR код можно нанести на визитку.</p>
<p>Примеры текста, из которого можно сгенерировать QR код:</p>
<table>
	<thead>
			<tr>
					<th>Тип</th>
					<th>Пример</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Просто текст</td>
					<td><code>Просто текст</code></td>
			</tr>
			<tr>
					<td>Телефон</td>
					<td><code>tel:+79012345678</code></td>
			</tr>
			<tr>
					<td>Email</td>
					<td><code>mailto:blog@example.org</code></td>
			</tr>
			<tr>
					<td>Email с темой и текстом</td>
					<td><code>mailto:blog@example.org?subject=something&amp;body=lalala</code></td>
			</tr>
			<tr>
					<td>SMS</td>
					<td><code>SMSTO:+79012345678:</code></td>
			</tr>
			<tr>
					<td>Ссылка на сайт</td>
					<td><code>https://vodolaz095.ru/</code></td>
			</tr>
			<tr>
					<td>Настройки WIFI (шифрование - WPA/WPA2)</td>
					<td><code>WIFI:S:my_wifi;T:WPA;P:12345678;;</code></td>
			</tr>
	</tbody>
</table>
<p>На этой странице есть простой генератор QR кодов, с помощью которого вы можете нагенерировать свои коды.
Код библиотеки генерации QR кода взят отсюда: <a href="https://github.com/davidshimjs/qrcodejs">https://github.com/davidshimjs/qrcodejs</a>.</p>
<form id="form_qr">
<p><label for="text">Введите текст для QR кода:</label></p>
<p><input style="color: #1e0010;width:100%" id="text" type="text" placeholder="https://vodolaz095.ru/" value="https://vodolaz095.ru/"/></p>
<p>
    <input style="color:#1e0010;width:100%" type="submit" value="Сгенерировать QR code">
    <input style="color:red;width:100%" type="reset" value="Cбросить">
</p>
</form>
<div id="qrcode3" style="width:300px; height:300px; margin-top:15px;"></div>
<script src="/qrcode/qrcode.min.js"></script>
<script type="text/javascript">
const elText = document.getElementById("text");
const qrcode = new QRCode(document.getElementById("qrcode3"), {width : 300, height : 300});
const qrForm = document.getElementById("form_qr");
qrForm.addEventListener('submit', function(event) {
    event.preventDefault();
        if (!elText.value) {
            alert("Введите текст для генерации QR кода!");
            elText.focus();
            return false;
        }
    qrcode.makeCode(elText.value);
    return false;
});
</script>
]]></content></item><item><title>LINUX - КОГДА ЗАЛИПАЕТ CAPS LOCK</title><link>https://vodolaz095.ru/linux_caps_stuck/</link><pubDate>Sat, 05 Jul 2025 14:54:11 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/linux_caps_stuck/</guid><description>В статье рассказывается, как с помощью консольной утилиты xdotool можно включать и выключать CAPS-LOCK</description><content type="text/html" mode="escaped"><![CDATA[<p>Сначала надо установить утилиту xdootool:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo dnf install -y xdotool
</span></span></code></pre></div><p>Далее, можно создать скрипт <code>/usr/bin/CAPS</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>xdotool key Caps_Lock
</span></span></code></pre></div><p>и когда в следующий раз застрянет клавиша CAPS LOCK, то можно просто ввести команду CAPS</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ CAPS
</span></span></code></pre></div><p>и, надеюсь, CAPS-LOCK отключится.</p>
]]></content></item><item><title>Systemd - скрипт для включения и выключения юнита одной командой</title><link>https://vodolaz095.ru/systemd_toggle/</link><pubDate>Sat, 18 Jan 2025 13:31:12 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/systemd_toggle/</guid><description>В статье представлен образец скрипта, чтобы включать или выключать systemd unit одной командой.</description><content type="text/html" mode="escaped"><![CDATA[<p>Допустим, у вас есть OpenVPN соединение с работой (systemd-unit <code>openvpn-client@work.service</code>), и вы хотите включать
или выключать его одной командой:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ connect_work
</span></span></code></pre></div><p>Для этого можно написать такой скрипт (и разместить его, допустим, в <code>/usr/bin/connect_work</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set -e
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>service<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;openvpn-client@work.service&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>systemctl -q is-active <span style="color:#e6db74">&#34;</span>$service<span style="color:#e6db74">&#34;</span><span style="color:#f92672">)</span> <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>	echo <span style="color:#e6db74">&#34;Stopping </span>$service<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span>	sudo systemctl stop <span style="color:#e6db74">&#34;</span>$service<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>	echo <span style="color:#e6db74">&#34;Starting </span>$service<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span>	sudo systemctl start <span style="color:#e6db74">&#34;</span>$service<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>systemctl status <span style="color:#e6db74">&#34;</span>$service<span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><p>Чтобы sudo не спрашивало постоянно пароль для эскалации прав, пользователю можно разрешить выполнять эти команды
без запроса пароля, создав файл в <code>/etc/sudoers.d/vpn</code> с таким содержанием.</p>
<pre tabindex="0"><code>%vodolaz095 ALL=NOPASSWD: /usr/bin/systemctl start openvpn-client@work.service, /usr/bin/systemctl stop openvpn-client@work.service, /usr/bin/systemctl restart openvpn-client@work.service
</code></pre><p>Тут:</p>
<ul>
<li><code>%vodolaz095</code> - группа пользователя, которому можно совершать данные действия</li>
<li><code>ALL=NOPASSWD:</code> выключает проверку пароля при вызове sudo для этих команд</li>
<li><code>/usr/bin/systemctl start openvpn-client@work.service, ...</code> - список комманд, которые может вводить пользователь с эскалацией прав</li>
</ul>
<p>Совет - файл можно создать любым текстовым редактором, но лучше всего использовать
<a href="https://man7.org/linux/man-pages/man8/visudo.8.html">visudo</a> - оно проверяет корректность синтаксиса.</p>
<p>Создать разрешения для использования vpn можно этой командой</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo visudo /etc/sudoers.d/vpn 
</span></span></code></pre></div><p>Как итог, любой пользователь из группы <code>vodolaz095</code> может вызвать команду <code>$ connect_work</code> и включить или выключить
systemd юнит <code>openvpn-client@work.service</code>.</p>
<p>Образец скрипта также опубликован тут:
<a href="https://gist.github.com/vodolaz095/484efe4fbd385185d50c0e454d32eabe">https://gist.github.com/vodolaz095/484efe4fbd385185d50c0e454d32eabe</a></p>
]]></content></item><item><title>Как слушать радио с помощью RTL-SDR v4 в Fedora Linux 43</title><link>https://vodolaz095.ru/rtl_sdr4/</link><pubDate>Sun, 18 Feb 2024 16:22:49 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/rtl_sdr4/</guid><description>Как слушать радио с помощью программно определяемого USB радиоприёмника RTL-SDR v4 в Fedora Linux&amp;#34;</description><enclosure url="https://vodolaz095.ru/images/rtl_sdr/pcb_v4_features_compressed_2-1024x1024.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/rtl_sdr/pcb_v4_features_compressed_2-1024x1024.jpg" width="1024" height="1024"/><media:content url="https://vodolaz095.ru/images/rtl_sdr/pcb_v4_features_compressed_2-1024x1024.jpg" medium="image" type="image/jpeg" width="1024" height="1024"><media:title type="html">/images/rtl_sdr/pcb_v4_features_compressed_2-1024x1024.jpg</media:title></media:content><enclosure url="https://vodolaz095.ru/images/rtl_sdr/gqrx_settings.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/rtl_sdr/gqrx_settings.png" width="523" height="542"/><media:content url="https://vodolaz095.ru/images/rtl_sdr/gqrx_settings.png" medium="image" type="image/png" width="523" height="542"><media:title type="html">/images/rtl_sdr/gqrx_settings.png</media:title></media:content><enclosure url="https://vodolaz095.ru/images/rtl_sdr/gqrx_overview.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/rtl_sdr/gqrx_overview.png" width="1920" height="1044"/><media:content url="https://vodolaz095.ru/images/rtl_sdr/gqrx_overview.png" medium="image" type="image/png" width="1920" height="1044"><media:title type="html">/images/rtl_sdr/gqrx_overview.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p>Программно-определяемый радиоприёмник <a href="https://www.rtl-sdr.com/rtl-sdr-blog-v4-dongle-initial-release/">RTL SDR4</a>
можно использовать для приёма радиопередач с помощью компьютера.
Можно ловить как радиостанции FM диапазона, так же, при наличии правильный антенны (в моём случае это было около 300 метров
недорогого медного одножильного провода, натянутого между елей), можно ловить длинноволновые и средневолновые
радиостанции, включая полумифическую УВБ-76 из болот Ленинградской области. Кстати, её трансляции можно послушать в канале
Телеграма - <a href="https://t.me/uvb76logs">https://t.me/uvb76logs</a></p>
<p>Официально устройство можно купить через <a href="https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/">сайт</a>,
но, в моём случае я купил его через Ozon.</p>
<p><img src="/images/rtl_sdr/pcb_v4_features_compressed_2-1024x1024.jpg" alt="Как должен выглядеть оригинальный RTL SDR v4"></p>
<p>На момент написания статьи я использовал дистрибутив Fedora 40.</p>
<p>Если включить отладочный вывод ядра с помощью <code>$ sudo dmesg -w</code>, то при подключении
устройства к USB порту третьей версии (обычно - синий), то в лог пишеться такая информация:</p>
<pre tabindex="0"><code>[ 5217.819868] usb 1-3.1.1: new high-speed USB device number 17 using xhci_hcd
[ 5217.907841] usb 1-3.1.1: New USB device found, idVendor=0bda, idProduct=2838, bcdDevice= 1.00
[ 5217.907845] usb 1-3.1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 5217.907846] usb 1-3.1.1: Product: Blog V4
[ 5217.907846] usb 1-3.1.1: Manufacturer: RTLSDRBlog
[ 5217.907847] usb 1-3.1.1: SerialNumber: 00000001
[ 5273.611541] usb 1-3.1.1: USB disconnect, device number 17
[ 5278.637976] usb 1-1: new high-speed USB device number 18 using xhci_hcd
[ 5278.778039] usb 1-1: New USB device found, idVendor=0bda, idProduct=2838, bcdDevice= 1.00
[ 5278.778055] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 5278.778060] usb 1-1: Product: Blog V4
[ 5278.778063] usb 1-1: Manufacturer: RTLSDRBlog
[ 5278.778067] usb 1-1: SerialNumber: 00000001
[ 5352.391304] usb 1-1: USB disconnect, device number 18
</code></pre><p>С USB портами второй версии (чёрные) устройство у меня не заработало.</p>
<p>Далее, надо установить требуемое программное обеспечение:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo dnf install gqrx
</span></span></code></pre></div><p>В моём случае установило довольно много зависимостей:</p>
<pre tabindex="0"><code>Установка:
gqrx                      x86_64 2.17.5-1.fc40                      updates 672 k
Установка зависимостей:
SoapySDR                  x86_64 0.8.1-11.fc40                      fedora  161 k
airspyone_host            x86_64 1.0.10-6.fc40                      fedora   64 k
boost-chrono              x86_64 1.83.0-5.fc40                      updates  25 k
boost-program-options     x86_64 1.83.0-5.fc40                      updates 108 k
boost-serialization       x86_64 1.83.0-5.fc40                      updates 133 k
boost-thread              x86_64 1.83.0-5.fc40                      updates  61 k
flexiblas                 x86_64 3.4.5-1.fc40                       updates  26 k
flexiblas-netlib          x86_64 3.4.5-1.fc40                       updates 3.3 M
flexiblas-openblas-openmp x86_64 3.4.5-1.fc40                       updates  17 k
gnuradio                  x86_64 3.10.9.2-2.fc40                    fedora  3.0 M
google-cpu_features       x86_64 0.7.0-6.fc40                       fedora   36 k
gr-funcube                x86_64 1.0.0-28.20220130gitbf71b979.fc40  fedora  156 k
gr-iqbal                  x86_64 0.38.2-26.20210210gitfbee239a.fc40 fedora  107 k
gr-osmosdr                x86_64 0.2.5-4.fc40                       fedora  369 k
gsl                       x86_64 2.7.1-8.fc40                       fedora  1.1 M
hackrf                    x86_64 2024.02.1-1.fc40                   fedora   89 k
hidapi                    x86_64 0.14.0-4.fc40                      fedora   46 k
libgfortran               x86_64 14.2.1-3.fc40                      updates 940 k
libosmo-dsp               x86_64 0.3-22.fc40                        fedora   25 k
libquadmath               x86_64 14.2.1-3.fc40                      updates 202 k
openblas                  x86_64 0.3.26-4.fc40                      fedora   39 k
openblas-openmp           x86_64 0.3.26-4.fc40                      fedora  5.1 M
portaudio                 x86_64 19-43.fc40                         fedora   93 k
python3-click-plugins     noarch 1.1.1-19.fc40                      fedora   17 k
python3-devel             x86_64 3.12.8-2.fc40                      updates 314 k
python3-gnuradio          x86_64 3.10.9.2-2.fc40                    fedora  3.6 M
python3-mako              noarch 1.2.3-6.fc40                       fedora  215 k
python3-numpy             x86_64 1:1.26.4-3.fc40                    updates 7.4 M
python3-numpy-f2py        x86_64 1:1.26.4-3.fc40                    updates 537 k
python3-paste             noarch 3.7.1-3.fc40                       fedora  643 k
python3-pooch             noarch 1.8.2-2.fc40                       updates 135 k
python3-pyOpenSSL         noarch 23.2.0-3.fc40                      fedora  126 k
python3-pyopengl          x86_64 3.1.7-6.fc40                       fedora  4.1 M
python3-pyqtgraph         noarch 0.13.3-6.fc40                      fedora  1.4 M
python3-pyzmq             x86_64 25.1.2-1.fc40                      updates 464 k
python3-scipy             x86_64 1.11.3-11.fc40                     updates  16 M
python3-tempita           noarch 0.5.2-12.fc40                      fedora   45 k
python3-thrift            x86_64 0.15.0-4.fc40                      fedora  189 k
python3-tkinter           x86_64 3.12.8-2.fc40                      updates 410 k
qwt-qt5                   x86_64 6.2.0-5.fc40                       fedora  577 k
rtl-sdr                   x86_64 0.6.0^20230921git1261fbb2-3.fc40   fedora   96 k
sdl12-compat              x86_64 1.2.68-2.fc40                      fedora   90 k
spdlog                    x86_64 1.12.0-4.fc40                      updates 117 k
tk                        x86_64 1:8.6.13-3.fc40                    fedora  1.6 M
uhd                       x86_64 4.6.0.0-3.fc40                     fedora  5.4 M
volk                      x86_64 3.1.1-1.fc40                       fedora  481 k
Установка слабых зависимостей:
hackrf-firmware           noarch 2024.02.1-1.fc40                   fedora   70 k
python3-beaker            noarch 1.12.1-5.fc40                      fedora  145 k
python3-crypto            x86_64 2.6.1-50.fc40                      fedora  564 k
python3-pip               noarch 23.3.2-2.fc40                      updates 3.2 M

Результат транзакции
================================================================================
Установка  51 Пакет

Объем загрузки: 64 M
Объем изменений: 278 M
</code></pre><p>Далее, после запуска <code>GQRX</code> вылезает окно настройки соединения с устройством.
В моём случае работали такие параметры:</p>
<p><img src="/images/rtl_sdr/gqrx_settings.png" alt="Настройки GQRX"></p>
<p>Интерфейс программы GQRX довольно простой - я разобрался, с Божией помощью, за пару часов методом научного тыка.</p>
<p><img src="/images/rtl_sdr/gqrx_overview.png" alt="Интерфейс GQRX"></p>
<p>На частоте <code>144.800.800</code> (в модуляции Narrow Frequency Modulation - NFM) можно перехватить пакетную передачу данных в APRS формате.</p>
<pre tabindex="0"><code>21:31:01$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}R2DFR-15&gt;APHFPG,TCPIP,R2BBS-3*:&gt;HF Pager &gt; APRS Gate MSK 7.175
21:31:46$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2AKT-13&gt;APRX29,TCPIP,RW3DKE-1*:!5556.34N/03758.45E_c180s001g003t020r...p...P...h00b.....Weather in Shchyolkovo: Temperature =&gt; -6.8 C; Wind =&gt; 0.4 m/s, S; Gust =&gt; 1.2 m/s; Humidity =&gt; 0 %; Pressure =&gt; n/a mmHg
21:31:47$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2DFR-4&gt;APWW10,TCPIP,RW3DKE-1*:@183140h5544.05N/03724.46Ey!INSERVICE!R2DFR@ya.ru  RADIOCULT.SU!W84!
21:32:20$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}RUA017&gt;APWW27,TCPIP,R2BBS-3*:!5628.47N/03818.35E- +DIGITAL MODE
21:38:28$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2DFR-13&gt;APRS,TCPIP,RW3DKE-1*:=5543.38N/03723.83EW WX_Kuntsevo=-4.9C1.0%744.62mmHg28.3C
21:38:29$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}R2DFR-13&gt;APRS,TCPIP,R2BBS-3*:=5543.38N/03723.83EW WX_Kuntsevo=-4.9C1.0%744.62mmHg28.3C
21:39:00$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RR3AL&gt;APMI01,TCPIP,RW3DKE-1*:=5528.30N/03717.74ErRepeater 145.700 MHz -0.600kHz FM/C4FM T94.8 H=60m P=50W, Admin R2AE, Troitsk, New Moscow
21:39:07$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
@181838z5552.66N/03732.47E#PHG5335800-LETIA MSK GM350 WX3in1Plus2,T=11.6C op.Anton r2bbs@rae.su
21:44:58$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2AYZ-D&gt;APDG03,TCPIP,RW3DKE-1*:!5545.59ND03744.30E&amp;/A=00000070cm MMDVM Voice (DMR) 439.66000MHz -5.0000MHz, ARK Moscow
21:45:09$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}RK3FWA&gt;APRS,TCPIP,R2BBS-3*:=5620.15N/03732.36ErRK3FWA FRN Node Regions | 145.775/145.175 #127.3 | More info: digital.hamcom.ru
21:45:37$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2DFR-12&gt;APHFPG,TCPIP,RW3DKE-1*:/250949z5543.24N/03723.79ErHF Pager APRS Gate 7.175 USB
21:46:07$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}UB3FBR-15&gt;APRS,TCPIP,R2BBS-3*:=5509.85N/03726.17E- op.Yuri, QSX:145.500, avr-eth-&gt;linux Node-RED
21:46:21$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RZ3DKH-D&gt;APDG03,TCPIP,RW3DKE-1*:!5555.89N\03751.36E-/A=00000070cm MMDVM Voice (DMR) 438.80000MHz -5.0000MHz, APRS for DMRGateway
21:46:22$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RZ3DKH-D&gt;APDG03,TCPIP,RW3DKE-1*:&gt;Powered by WPSD (https://wpsd.radio)          
21:47:51$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2DFR-4&gt;APWW10,TCPIP,RW3DKE-1*:&gt;KO85qr/y 145.650/ 145.050 DMR TX/RX
21:48:05$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}RC3C-17&gt;APLX10,TCPIP,R2BBS-3*:=5528.31N/03718.10E?Server:  Debian GNU/Linux 10 (buster)  Linux 4.19.0-25-amd64 | 18.02.2025  21:48:01 up 32 days, 12:19,  1 user,  load average: 0.09, 0.12, 0.16
21:48:29$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2DFR-13&gt;APRS,TCPIP,RW3DKE-1*:=5543.38N/03723.83EW WX_Kuntsevo=-4.9C1.0%744.56mmHg28.6C          
22:23:36$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}R2DFR-13&gt;APRS,TCPIP,R2BBS-3*:=5543.38N/03723.83EW WX_Kuntsevo=-4.7C1.0%744.47mmHg29.0C
22:23:40$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2ATJ&gt;APBM1S,TCPIP,RW3DKE-1*:@181923z5546.80N/03727.00EQPHG1000MMDVM MMDVM HS Hat 438.8800/438.8800 CC1
22:24:22$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}UB3IBX-1&gt;APBM1S,TCPIP,RW3DKE-1*:@181924z5552.77N/03733.76EQPHG1000MMDVM DMO 144.8875/144.8875 CC1
22:24:22$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RK3FWD-A&gt;APJI40,TCPIP,RW3DKE-1*:!5549.18ND03736.70E&amp;RNG0002 1.2 Voice 1280
22:24:23$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RK3FWD-A&gt;APJI40,TCPIP,RW3DKE-1*:&lt;IGATE,MSG_CNT=0,LOC_CNT=0
22:24:24$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RK3FWD-S&gt;APJIO4,TCPIP,RW3DKE-1*:;RK3FWD A *281003z5549.18ND03736.70EaRNG0002 1.2 Voice 1280
22:24:25$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}RZ3DKH-2&gt;APBM1S,TCPIP,RW3DKE-1*:@181924z5555.89N/03751.37ErPHG1000MMDVM MMDVM HS Dual Hat 438.8000/433.8000 CC1
22:24:29$ fm R2BBS-3 to APMI06-0 via RW3DKE-1 UI  PID=F0
}R2ADU-2&gt;APBM1S,TCPIP,R2BBS-3*:@181924z5552.27N/03736.63ErPHG1300MMDVM MMDVM HS Dual Hat 145.5750/144.9750 CC1
22:25:03$ fm RW3DKE-1 to APU25N-0 via WIDE1-1 UI^ PID=F0
}R2AYZ-D&gt;APDG03,TCPIP,RW3DKE-1*:!5545.59ND03744.30E&amp;/A=00000070cm MMDVM Voice (DMR) 439.66000MHz -5.0000MHz, ARK Moscow          
</code></pre><p>О ней подробнее написано у <a href="https://eax.me/sdr-aprs/">https://eax.me/sdr-aprs/</a>
Как видно по повторяющимся позывным <code>RW3DKE</code>, я похоже поймал работу шлюза Михаила Абрашина, о котором написано тут
<a href="http://aprs.qrz.ru/art/rw3dke/01.php">http://aprs.qrz.ru/art/rw3dke/01.php</a></p>
]]></content></item><item><title>Curl - работаем с электронной почтой</title><link>https://vodolaz095.ru/curl_email/</link><pubDate>Fri, 19 Jan 2024 13:54:20 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/curl_email/</guid><description>В статье рассказывается, как с помощью консольной утилиты curl можно проверять и отправлять электронную почту.</description><content type="text/html" mode="escaped"><![CDATA[<h1 id="проверить-почту-по-протоколу-imap">Проверить почту по протоколу IMAP<a href="#%d0%bf%d1%80%d0%be%d0%b2%d0%b5%d1%80%d0%b8%d1%82%d1%8c-%d0%bf%d0%be%d1%87%d1%82%d1%83-%d0%bf%d0%be-%d0%bf%d1%80%d0%be%d1%82%d0%be%d0%ba%d0%be%d0%bb%d1%83-imap" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Чтобы проверить, какие расширения поддерживает IMAP сервер, можно вызвать такую команду</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env
</span></span></span><span style="display:flex;"><span>auth<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;vodolaz095:password&#34;</span>
</span></span><span style="display:flex;"><span>host<span style="color:#f92672">=</span>imaps://imap.example.org
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>curl --url $host --user $auth -X <span style="color:#e6db74">&#34;CAPABILITY&#34;</span>
</span></span></code></pre></div><p>Dovecot сервер версии <code>2.4.4</code>, с которым я эксперементировал, поддерживал эти функции:</p>
<pre tabindex="0"><code>CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE ACL RIGHTS=texk
</code></pre><p>Эта команды показывают все доступные директории:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>auth<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;vodolaz095:password&#34;</span>
</span></span><span style="display:flex;"><span>host<span style="color:#f92672">=</span>imaps://imap.example.org
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>curl --url $host --user $auth -X <span style="color:#e6db74">&#39;LIST &#34;&#34; &#34;*&#34;&#39;</span>
</span></span></code></pre></div><p>Эта команда считает сообщения в директории INBOX:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>auth<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;vodolaz095:password&#34;</span>
</span></span><span style="display:flex;"><span>host<span style="color:#f92672">=</span>imaps://imap.example.org
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>curl --url $host --user $auth -X <span style="color:#e6db74">&#39;STATUS INBOX (MESSAGES)&#39;</span>
</span></span></code></pre></div><p>Другие примеры опубликованы тут: <a href="https://everything.curl.dev/usingcurl/imap.html">https://everything.curl.dev/usingcurl/imap.html</a></p>
<h1 id="отправить-почту-по-smtp-submission-протоколу">Отправить почту по SMTP Submission протоколу<a href="#%d0%be%d1%82%d0%bf%d1%80%d0%b0%d0%b2%d0%b8%d1%82%d1%8c-%d0%bf%d0%be%d1%87%d1%82%d1%83-%d0%bf%d0%be-smtp-submission-%d0%bf%d1%80%d0%be%d1%82%d0%be%d0%ba%d0%be%d0%bb%d1%83" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Сначала надо создать текстовой файл <code>email.txt</code> с письмом, которое мы хотим послать.</p>
<pre tabindex="0"><code>
From: vodolaz095 &lt;sender@example.org&gt;
To: vodolaz095 &lt;receiver@example.org&gt;
Subject: проверка почтой
Date: Mon, 17 Dec 2024 08:45:16

Проверка почты, если вы не получили это письмо, то я отправлю его ещё один раз.
Всех благ.
С уважением, Анатолий.
</code></pre><p>Потом письмо можно отправить такой командой</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>auth<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;vodolaz095:password&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>curl smtp://smtp.example.org <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --ssl-reqd <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --user $auth <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --mail-from sender@example.org <span style="color:#ae81ff">\ </span>
</span></span><span style="display:flex;"><span>  --mail-rcpt receiver@example.org <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --upload-file email.txt
</span></span></code></pre></div><p>Другие примеры опубликованы тут: <a href="https://everything.curl.dev/usingcurl/smtp.html">https://everything.curl.dev/usingcurl/smtp.html</a></p>
]]></content></item><item><title>Не Zабудем!</title><link>https://vodolaz095.ru/z/</link><pubDate>Tue, 08 Mar 2022 10:41:06 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/z/</guid><description>#ZаПобеду Они защищают тебя и мир. Поддержим наших парней. Они это услышат!</description><enclosure url="https://vodolaz095.ru/images/z.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/z.jpg" width="1242" height="1242"/><media:content url="https://vodolaz095.ru/images/z.jpg" medium="image" type="image/jpeg" width="1242" height="1242"><media:title type="html">/images/z.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/z.jpg" alt="ZaПобеду.png"></p>
<p>#ZаПобеду Они защищают тебя и мир. Поддержим наших парней. Они это услышат!</p>
<p>UPD: почему я поддерживаю операцию по денацификации Украины:</p>
<pre tabindex="0"><code>Этот документ предназначен тем, 
у кого еще есть вопросы, зачем Россия проводит операцию, против кого и против чего. Когда будете изучать материалы преступлений,
совершенных укрофашистами, вспоминайте материалы Нюрнбергского процесса и сравнивайте. 
А прямо сейчас эти преступления происходят по всей Украине, когда укровоенными и 
добробатами загоняется тяжелое вооружение в города, происходят убийства и грабежи, 
отстреливают своих граждан, принимая их за диверсантов, взрывают мосты и рушат свою же инфраструктуру. 
При этом всем украинцам конкретно объявлено, что России «богатства Украины» даром не нужны, она сама еще тот подарочек, 
от которого 8 лет мозг взрывается. В общем, читайте, делитесь, просвещайте…
</code></pre><p>(с) <a href="https://t.me/kremlinprachka/17367">https://t.me/kremlinprachka/17367</a>
Перевод документа на английский язык: <a href="https://t.me/surf_noise1/6669">https://t.me/surf_noise1/6669</a></p>
<p>Фотография предоставлена <a href="https://t.me/rt_russian/97829">https://t.me/rt_russian/97829</a></p>
]]></content></item><item><title>Работайте, братья!</title><link>https://vodolaz095.ru/v/</link><pubDate>Mon, 07 Mar 2022 19:29:06 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/v/</guid><description>#ZаПобеду Они защищают тебя и мир. Поддержим наших парней. Они это услышат!</description><enclosure url="https://vodolaz095.ru/images/v.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/v.jpg" width="1242" height="1242"/><media:content url="https://vodolaz095.ru/images/v.jpg" medium="image" type="image/jpeg" width="1242" height="1242"><media:title type="html">/images/v.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/v.jpg" alt="Работайте, братья!"></p>
<p>Я горжусь Россией. #ZаПобеду.</p>
<p>UPD: почему я поддерживаю операцию по денацификации Украины:</p>
<pre tabindex="0"><code>Этот документ предназначен тем, 
у кого еще есть вопросы, зачем Россия проводит операцию, против кого и против чего. Когда будете изучать материалы преступлений,
совершенных укрофашистами, вспоминайте материалы Нюрнбергского процесса и сравнивайте. 
А прямо сейчас эти преступления происходят по всей Украине, когда укровоенными и 
добробатами загоняется тяжелое вооружение в города, происходят убийства и грабежи, 
отстреливают своих граждан, принимая их за диверсантов, взрывают мосты и рушат свою же инфраструктуру. 
При этом всем украинцам конкретно объявлено, что России «богатства Украины» даром не нужны, она сама еще тот подарочек, 
от которого 8 лет мозг взрывается. В общем, читайте, делитесь, просвещайте…
</code></pre><p>(с) <a href="https://t.me/kremlinprachka/17367">https://t.me/kremlinprachka/17367</a>
Перевод документа на английский язык: <a href="https://t.me/surf_noise1/6669">https://t.me/surf_noise1/6669</a></p>
<p>Фотография предоставлена <a href="https://t.me/rt_russian/97829">https://t.me/rt_russian/97829</a></p>
]]></content></item><item><title>systemd - как работать с NodeJS сервисом, у которого утекает память?</title><link>https://vodolaz095.ru/systemd-memleaky-nodejs/</link><pubDate>Thu, 18 Feb 2021 18:26:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/systemd-memleaky-nodejs/</guid><description>В статье рассмотрен один из вариантов борьбы с утечками памяти в приложении на NodeJS, которые начинаются после нескольких часов работы</description><enclosure url="https://vodolaz095.ru/images/systemd_memleaky_node.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/systemd_memleaky_node.png" width="1073" height="736"/><media:content url="https://vodolaz095.ru/images/systemd_memleaky_node.png" medium="image" type="image/png" width="1073" height="736"><media:title type="html">/images/systemd_memleaky_node.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/systemd_memleaky_node.png" alt="systemd_memleaky_node.png"></p>
<p>Всем хороша среда выполнения Javascript NodeJS, но у неё есть три недостатка.</p>
<ul>
<li>Первый недостаток - много низкокачественных NPM модулей, включая те, в которых происходят утечки памяти.</li>
<li>Второй недостаток - обычная программа требует множество модулей, и обязательно среди них будет несколько, в которых утекает память.</li>
<li>Третий недостаток - иногда утечки памяти начинаются после часов, а то и суток работы программы.</li>
</ul>
<p>Что с этим делать?
Можно потратить много времени, разобраться, где утекает память, написать исправление к модулю и послать Pull-Request разработчикам модуля,
который они скорее всего проигнорируют, так как у них проблема не воспроизводится, или же они, как обычно, не видят в этом проблему.
А можно просто перезапустить программу через несколько часов работы, когда начинают появляться утечки памяти.</p>
<p>Для примера, рассмотрим почтовый сервер на основе <a href="https://haraka.github.io/">https://haraka.github.io/</a>, который, после запуска
потребляет 56.8 мегабайт памяти, а через несколько суток - уже 180 мегабайт.</p>
<p>Данный сервер запускается с помощью systemd, используя этот unit файл:</p>
<pre tabindex="0"><code>[Unit]
Description=Haraka inbound STMP server
After=network.target

[Service]
Type=simple

User=haraka
Group=haraka

WorkingDirectory=/opt/haraka/haraka-inbound
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=1s
Environment=NODE_ENV=production

# Hardening
ProtectHome=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
</code></pre><p>Как его можно перезапустить, допустим, раз в час, используя systemd?</p>
<p>Для этого достаточно почитать документацию к systemd, и найти этот параметр конфигурации:</p>
<p><a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#RuntimeMaxSec">https://www.freedesktop.org/software/systemd/man/systemd.service.html#RuntimeMaxSec</a></p>
<pre tabindex="0"><code>RuntimeMaxSec=

Configures a maximum time for the service to run. If this is used 
and the service has been active for longer than the specified time it is terminated 
and put into a failure state. Note that this setting does not have any effect 
on Type=oneshot services, as they terminate immediately after activation 
completed. Pass &#34;infinity&#34; (the default) to configure no runtime limit.
</code></pre><p>То есть, задав этот параметр равным 3600, мы добьёмся того, что systemd будет автоматически останавливать, и
снова запускать почтовый сервер каждый час. Так как Haraka написан неожиданно хорошо для nodejs приложений,
он не только быстро перезагружается, он ещё знает, как закончить все транзакции перед перезапуском, и поэтому
ни одно письмо не пропадёт.</p>
<p>Такой unit файл был в итоге создан для почтового сервера:</p>
<pre tabindex="0"><code>
[Unit]
Description=Haraka inbound STMP server
After=network.target

[Service]
Type=simple

User=haraka
Group=haraka

WorkingDirectory=/opt/haraka/haraka-inbound
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=1s
Environment=NODE_ENV=production

# Gracefully restart service every hour to prevent memleaks
RuntimeMaxSec=3600

# Hardening
ProtectHome=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
</code></pre><p>Теперь почтовый сервер перезапускается раз в час, и в среднем потребляет около 60 мегабайт памяти.</p>
]]></content></item><item><title>NodeJS и ldapJS - как авторизовать пользователя и прочитать свой профиль из базы данных OpenLDAP?</title><link>https://vodolaz095.ru/nodejs-openldap/</link><pubDate>Tue, 16 Feb 2021 19:14:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/nodejs-openldap/</guid><description>Задача состояла в том, что пользователь вводит логин (в виде user1) и пароль в NodeJS приложении. И программа должна проверить, угадал ли пользователь пароль, а потом программа должна загрузить все данные о пользователе из базы данных OpenLDAP.</description><enclosure url="https://vodolaz095.ru/images/ldap_idm_integration.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/ldap_idm_integration.png" width="500" height="500"/><media:content url="https://vodolaz095.ru/images/ldap_idm_integration.png" medium="image" type="image/png" width="500" height="500"><media:title type="html">/images/ldap_idm_integration.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/ldap_idm_integration.png" alt="ldap_idm_integration.png"></p>
<p>Недавно на работе у меня было такое задание - соединить NodeJS приложение с OpenLDAP сервером, обладающем следующей структурой данных:
в корне - структура Organization Unit с профилями пользователями, доступная, для примера, по DN
<code>ou=people,dc=vodolaz095,dc=ru</code> и имеющая следующую структуру:</p>
<pre tabindex="0"><code>dn: ou=people,dc=vodolaz095,dc=ru
objectclass: organizationalUnit
ou: people
</code></pre><p>В этой структуре - уже были профили пользователей - например, <code>uid=user1,ou=people,dc=vodolaz095,dc=ru</code>, <code>uid=user2,ou=people,dc=vodolaz095,dc=ru</code> и т.д.</p>
<p>Аккаунты пользователей принадлежали к типам <code>posixAccount</code>, <code>inetOrgPerson</code>, <code>organizationalPerson</code>, <code>person</code>.</p>
<p>Аккаунты пользователей выглядели примерно вот так:</p>
<pre tabindex="0"><code>dn: uid=user1,ou=people,dc=vodolaz095,dc=ru
cn: Jane Doe
gidnumber: 1000
givenname:  Jane
homedirectory: /home/janedoe
initials: JS
loginshell: /bin/bash
objectclass: posixAccount
objectclass: inetOrgPerson
objectclass: organizationalPerson
objectclass: person
mail: user1@vodolaz095.ru
sn: Doe
uid: user1
uidnumber: 1002
userpassword: {SSHA}HF3O9bpD+/a5iswMxRCwBgboZgV2Mkpp
</code></pre><p>При этом для каждого пользователя был задан пароль.</p>
<p>Задача состояла в том, что пользователь вводит логин (в виде <code>user1</code>) и пароль в NodeJS приложении.
И программа должна проверить, угадал ли пользователь пароль, а потом программа должна загрузить все
данные о пользователе из базы данных OpenLDAP.</p>
<p>Сначала я попробовал данный метод авторизации, который, к сожалению, требует наличия пользователя
<code>readonly</code> с полным доступом на чтение ко всей базе данных, что, по моему, не очень
безопасно - так как данный пользователь может прочитать все данные, включая те,
которые могут составлять коммерческую или же медицинскую тайну.
Также пароль пользователя <code>readonly</code> может измениться, и приложение перестанет работать.</p>
<p>Для работы по протоколу openLDAP я использовал следующую NodeJS библиотеку - <a href="https://www.npmjs.com/package/ldapjs">https://www.npmjs.com/package/ldapjs</a>.
Минимальный работающий образец исходного кода представлен тут:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldap</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ldapjs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldapClient</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ldap</span>.<span style="color:#a6e22e">createClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">url</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ldap://127.0.0.1:389&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;cn=readonly,dc=vodolaz095,dc=ru&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">password</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;readonly&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">bind</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">username</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">password</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;bind performed&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">search</span>(<span style="color:#e6db74">&#39;ou=people,dc=vodolaz095,dc=ru&#39;</span>, {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">filter</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`(uid=vodolaz095)`</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">scope</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;one&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;uid&#39;</span>, <span style="color:#e6db74">&#39;dn&#39;</span>, <span style="color:#e6db74">&#39;cn&#39;</span>, <span style="color:#e6db74">&#39;mail&#39;</span>]
</span></span><span style="display:flex;"><span>    }, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">res</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;searchEntry&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">data</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// console.log(&#39;Data found&#39;, data);
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Data object&#39;</span>, <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>(<span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">object</span>, <span style="color:#66d9ef">null</span>, <span style="color:#ae81ff">2</span>));
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;error&#39;</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">error</span>){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;end&#39;</span>, <span style="color:#66d9ef">function</span> () {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Completed&#39;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>Как видно из кода, мы сначала &ldquo;биндимся&rdquo; к базе данных как пользователь <code>readonly</code> с
DN <code>cn=readonly,dc=vodolaz095,dc=ru</code>, потом ищем пользователя в коллекции
<code>ou=people,dc=vodolaz095,dc=ru</code> по фильтру <code>(uid=vodolaz095)</code>, и если мы его нашли,
то можно попробовать &ldquo;забиндиться&rdquo; как этот пользователь к базе данных, тем самым проверив пароль, подходит ли он.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldap</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ldapjs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldapClient</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ldap</span>.<span style="color:#a6e22e">createClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">url</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ldap://127.0.0.1:389&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">password</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;superSecretPasswordOfVodolaz095!&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#a6e22e">username</span>, <span style="color:#a6e22e">password</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;пароль подошёл, пользователь vodolaz095 авторизирован!&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p><strong>Можно ли как то упростить данную процедуру?</strong></p>
<p>Возможно ли не использовать пользователя <code>readonly</code>?</p>
<p>Можно ли минимизировать число запросов данных к базе данных ldap?</p>
<p>Ответ:</p>
<p><strong>Да, можно</strong></p>
<p>И сейчас я расскажу, как.
Оказывается, большинство openLDAP серверов позволяют авторизоваться (&ldquo;забиндиться&rdquo;) как искомый пользователь, и даже получить его профиль.</p>
<p>Рассмотрим данный код:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldap</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ldapjs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldapClient</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ldap</span>.<span style="color:#a6e22e">createClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">url</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ldap://127.0.0.1:389&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">password</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;thisIsVerySecureSecretPassword&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">bind</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">username</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">password</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;bind performed&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">search</span>(<span style="color:#e6db74">&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;</span>, { 
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">scope</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;base&#39;</span>, <span style="color:#75715e">// important
</span></span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;uid&#39;</span>, <span style="color:#e6db74">&#39;dn&#39;</span>, <span style="color:#e6db74">&#39;cn&#39;</span>, <span style="color:#e6db74">&#39;mail&#39;</span>]
</span></span><span style="display:flex;"><span>    }, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">res</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;searchEntry&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">data</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// console.log(&#39;Data found&#39;, data);
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Data object&#39;</span>, <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>(<span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">object</span>, <span style="color:#66d9ef">null</span>, <span style="color:#ae81ff">2</span>));
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;error&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;end&#39;</span>, <span style="color:#66d9ef">function</span> () {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;All passed&#39;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>С помощью этого кода мы напрямую пытаемся &ldquo;забиндиться&rdquo; к базе данных ldap как ограниченный пользователь, используя функцию
<code>ldapClient.bind(username,  password, function(error){....})</code>.</p>
<p>Если &ldquo;забиндиться&rdquo; получилось, то это означает, что пароль подошёл. Теперь надо получить профиль пользователя, и по умолчанию,
все пользователи ldap имеют права на чтение своего собственного профиля.
По крайней мере, так работает в контейнеризированной сборке openLDAP из <a href="https://github.com/osixia/docker-openldap">https://github.com/osixia/docker-openldap</a>.</p>
<p>После того, как мы забиндились, мы можем попробовать найти свой собственный профиль. Это можно сделать с помощью данной функции:</p>
<pre tabindex="0"><code>    ldapClient.search(&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;, { 
      scope: &#39;base&#39;, 
      attributes: [&#39;uid&#39;, &#39;dn&#39;, &#39;cn&#39;, &#39;mail&#39;]
    }, function (error, res) {....})
</code></pre><p>В итоге, мы получаем всю информацию из профиля пользователя в базе данных openldap,
при этом приложение использует только пароль, который предоставил сам пользователь,
доступ на чтение ко всей базе данных не нужен.</p>
]]></content></item><item><title>Как надо программировать на языке Golang</title><link>https://vodolaz095.ru/how-to-program-on-go/</link><pubDate>Sun, 23 Feb 2020 08:47:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/how-to-program-on-go/</guid><description>В глубинах исходных кодов одного из проприентарных продуктов, который я пилю по субботам...</description><enclosure url="https://vodolaz095.ru/images/kurov_go.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/kurov_go.png" width="1398" height="679"/><media:content url="https://vodolaz095.ru/images/kurov_go.png" medium="image" type="image/png" width="1398" height="679"><media:title type="html">/images/kurov_go.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/kurov_go.png" alt="исходный код"></p>
<p>В глубинах исходных кодов одного из проприентарных продуктов, который я пилю по субботам, я нашёл упоминение
Советского бога программирования <a href="/kurov/">А.В. Курова</a>,
который воспитал не одно поколение одарённых (не только в общепринятом смысле) программистов.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestWorkspace_WriteFile</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">GetWorkSpaceByID</span>(<span style="color:#a6e22e">workspaceIDForTests</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#e6db74">&#34;nobody&#34;</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">User</span>, <span style="color:#e6db74">&#34;wrong user&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#e6db74">&#34;unknown&#34;</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Company</span>, <span style="color:#e6db74">&#34;wrong company&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">config</span>.<span style="color:#a6e22e">Config</span>.<span style="color:#a6e22e">Hostname</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Server</span>, <span style="color:#e6db74">&#34;wrong server&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">DiskUsage</span>, <span style="color:#e6db74">&#34;wrong disk usage&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">NumberOfFiles</span>, <span style="color:#e6db74">&#34;wrong number of files&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#e6db74">&#34;0 bytes&#34;</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">HumanizeDiskUsage</span>(), <span style="color:#e6db74">&#34;wrong disk usage in human readable form&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;%s%s&#34;</span>, <span style="color:#a6e22e">config</span>.<span style="color:#a6e22e">Config</span>.<span style="color:#a6e22e">Path</span>.<span style="color:#a6e22e">Workspace</span>, <span style="color:#a6e22e">workspaceIDForTests</span>),
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">GetDirectoryPath</span>(),
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;wrong directory path&#34;</span>,
</span></span><span style="display:flex;"><span>	)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">FileExists</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">GetMetadataPath</span>(), <span style="color:#e6db74">&#34;metadata file not exists&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">True</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">DoBelongsToUser</span>(<span style="color:#e6db74">&#34;nobody&#34;</span>, <span style="color:#e6db74">&#34;unknown&#34;</span>), <span style="color:#e6db74">&#34;not belongs to user&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">False</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">DoBelongsToUser</span>(<span style="color:#e6db74">&#34;somebody&#34;</span>, <span style="color:#e6db74">&#34;unknown&#34;</span>), <span style="color:#e6db74">&#34;belongs to wrong user&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">False</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">DoBelongsToUser</span>(<span style="color:#e6db74">&#34;nobody&#34;</span>, <span style="color:#e6db74">&#34;Microsoft&#34;</span>), <span style="color:#e6db74">&#34;belongs to wrong user&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">False</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">DoBelongsToUser</span>(<span style="color:#e6db74">&#34;somebody&#34;</span>, <span style="color:#e6db74">&#34;Microsoft&#34;</span>), <span style="color:#e6db74">&#34;belongs to wrong user&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteFile</span>(<span style="color:#e6db74">&#34;test.txt&#34;</span>, []byte(<span style="color:#e6db74">&#34;this is test.txt file with some data in it. Как говорил мой преподаватель программирования Андрей Владимирович Куров, писать код надо так, чтобы моча ухом шла!&#34;</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>В тестовый файл пишется эта строка</p>
<pre tabindex="0"><code>
this is test.txt file with some data in it. 
Как говорил мой преподаватель программирования Андрей Владимирович Куров, 
писать код надо так, чтобы моча ухом шла!
</code></pre><p>В общем:</p>
<pre tabindex="0"><code>НЕПЫА́ЙТЕ АДУ́ЙТЕ!
</code></pre><p>(в переводе на Русский язык - &ldquo;не проверяйте, а думайте&rdquo;).</p>
<p>PS: По <code>git blame</code> этот код написал какой-то итальянец по имени Ricardo.</p>
]]></content></item><item><title>Как установить Centos 8 NetInstall если вылезает ошибка 'Error setting up base repository'</title><link>https://vodolaz095.ru/centos8_fix_error_setting_up_base_repository/</link><pubDate>Sun, 29 Dec 2019 16:52:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/centos8_fix_error_setting_up_base_repository/</guid><description>Чтобы исправить данную ошибку, образу надо сообщить путь до совместимого хранилища зависимостей. Проще всего сделать это так...&amp;#34;.</description><enclosure url="https://vodolaz095.ru/images/centos8/centos8_bug.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/centos8/centos8_bug.png" width="1024" height="768"/><media:content url="https://vodolaz095.ru/images/centos8/centos8_bug.png" medium="image" type="image/png" width="1024" height="768"><media:title type="html">/images/centos8/centos8_bug.png</media:title></media:content><enclosure url="https://vodolaz095.ru/images/centos8/centos8_fix.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/centos8/centos8_fix.png" width="1024" height="768"/><media:content url="https://vodolaz095.ru/images/centos8/centos8_fix.png" medium="image" type="image/png" width="1024" height="768"><media:title type="html">/images/centos8/centos8_fix.png</media:title></media:content><enclosure url="https://vodolaz095.ru/images/centos8/centos8_install.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/centos8/centos8_install.png" width="1024" height="768"/><media:content url="https://vodolaz095.ru/images/centos8/centos8_install.png" medium="image" type="image/png" width="1024" height="768"><media:title type="html">/images/centos8/centos8_install.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p>В интернете широко распространён дистрибутив Centos8, который можно легално загрузить на Ваш компьютер отсюда: <a href="https://www.centos.org/download/">https://www.centos.org/download/</a></p>
<p>В частности, для России лучше всего использовать хранилище яндекса:  <a href="https://mirror.yandex.ru/centos/8.0.1905/isos/x86_64/">https://mirror.yandex.ru/centos/8.0.1905/isos/x86_64/</a></p>
<p>На момент написание статьи в хранилище Яндекса присутсвует минимальный ISO образ версии 8.0.1905
(<a href="https://mirror.yandex.ru/centos/8.0.1905/isos/x86_64/CentOS-8-x86_64-1905-boot.iso)">https://mirror.yandex.ru/centos/8.0.1905/isos/x86_64/CentOS-8-x86_64-1905-boot.iso)</a>,
который при инсталяции загружает нужные зависимости из локальной сети или из сети Интернет.</p>
<p>В данном образа есть неприятная, но легко устранимая ошибка - 0016456: CentOS 8 Netinstall broken: &ldquo;Error setting up base repository&rdquo;,
которую похоже скоро решат - <a href="https://bugs.centos.org/view.php?id=16456">https://bugs.centos.org/view.php?id=16456</a>.
Но, на момент написания статьи,  ошибка ещё не исправлена, и образ не может загрузить требуемые зависимости для установки системы.</p>
<p>Проявляется эта ошибка так:
<img src="/images/centos8/centos8_bug.png" alt="centos8_bug.png"></p>
<p>Чтобы исправить данную ошибку, образу надо сообщить путь до совместимого хранилища зависимостей.Проще всего сделать это
так: во вкладке &ldquo;Installation source&rdquo; задать источник <a href="https://mirror.yandex.ru/centos/8.0.1905/BaseOS/x86_64/os/">https://mirror.yandex.ru/centos/8.0.1905/BaseOS/x86_64/os/</a>
( с протоколом HTTPS) и типом &ldquo;repository URL&rdquo;.</p>
<p><img src="/images/centos8/centos8_fix.png" alt="centos8_fix.png"></p>
<p>И установку можно продолжить как обычно.</p>
<p><img src="/images/centos8/centos8_install.png" alt="centos8_install.png"></p>
<p>Поздравляю всех с наступающим Новым, 2020 годом и окончанием десятилетия.</p>
]]></content></item><item><title>Как бороться с ошибкой read ECONNRESET в Ghost</title><link>https://vodolaz095.ru/fix-econnreset-in-ghost/</link><pubDate>Sun, 16 Jun 2019 15:22:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/fix-econnreset-in-ghost/</guid><description>Иногда мой блог вместо страницы выдавал ошибку 500, с отображением SQL запроса, который не прошёл. select count(distinct posts.id) as aggregate from posts where (posts.status = &amp;#39;published&amp;#39; and posts.page = false)</description><enclosure url="https://vodolaz095.ru/images/fix_econnreset_ghost.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/fix_econnreset_ghost.png" width="1075" height="717"/><media:content url="https://vodolaz095.ru/images/fix_econnreset_ghost.png" medium="image" type="image/png" width="1075" height="717"><media:title type="html">/images/fix_econnreset_ghost.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p>Иногда мой блог вместо страницы выдавал ошибку 500, с отображением SQL запроса, который не прошёл.
Это выглядело так:</p>
<p><img src="/images/fix_econnreset_ghost.png" alt="fix_econnreset_ghost.png"></p>
<p>Код ошибки мог меняться в замисимости от страницы, но почти всегда это был SQL запрос вроде такого</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#66d9ef">count</span>(<span style="color:#66d9ef">distinct</span> posts.id) <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">aggregate</span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">from</span> posts 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">where</span> (posts.status <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;published&#39;</span> <span style="color:#66d9ef">and</span> posts.page <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>)
</span></span></code></pre></div><p>Главное, что в нём всегда встречалось <code>read ECONNRESET</code>.</p>
<p>Причина неполадок состояла в том, что сервер базы данных закрывал соединение с блог платформой, так как оно,
по мнению сервера, не использовалось.
Данная неполадка чинится отправкой этих запросов на MariaDB сервер через консоль сервера MySQL/MariaDB:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> <span style="color:#66d9ef">GLOBAL</span> connect_timeout<span style="color:#f92672">=</span><span style="color:#ae81ff">28800</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> <span style="color:#66d9ef">GLOBAL</span> wait_timeout<span style="color:#f92672">=</span><span style="color:#ae81ff">28800</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> <span style="color:#66d9ef">GLOBAL</span> interactive_timeout<span style="color:#f92672">=</span><span style="color:#ae81ff">28800</span>;
</span></span></code></pre></div><p>Эти переменные задают критерии, используемые сервером для того, чтобы определить, какие соединения считаются неиспользуемыми,
и их можно разорвать. Я поставил значение в 8 часов (28800 секунд), и разрывы соединения пропали.
Подробнее о всех системных переменных базы данных <a href="https://mariadb.com/kb/ru/library/server-system-variables/">MariaDB</a>.</p>
<h1 id="обновление-1">Обновление 1.<a href="#%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-1" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>При перезагрузке сервера с базой данных, глобальные переменные к сожалению сбрасываются.
Одно из возможных решений проблемы ошибки с разрывом соединения было описано в этом комментарии
<a href="https://github.com/TryGhost/Ghost/issues/9739#issuecomment-406240883">https://github.com/TryGhost/Ghost/issues/9739#issuecomment-406240883</a>
Пользователь с ником <em>Toub</em> посоветовал периодически загружать главную страницу блога,
чтобы платформа посылала запросы к базе данных, и соединение восстанавливалось.</p>
<p>При этом техническая поддержка платформы Ghost показала особенную клиентоориентированность.
Z понял, что они ничем помочь не могут и не хотят, по их мнению, официальные образы докера с
неправильно работающим соединением с базой данных это нормально.
Поэтому, проблему я начал решать сам.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">async</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;async&#39;</span>); <span style="color:#75715e">// tested with  version &gt;=2.6.2
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">request</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;request&#39;</span>); <span style="color:#75715e">//  tested with version &gt;=2.88.0
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">async</span>.<span style="color:#a6e22e">doWhilst</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">callback</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">request</span>(<span style="color:#e6db74">&#39;https://blog.vodolaz095.life&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">response</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">callback</span>(<span style="color:#a6e22e">error</span>);
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">i</span> <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Test %s. Status code - %s&#39;</span>, <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">statusCode</span>); <span style="color:#75715e">// eslint-disable-line
</span></span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">callback</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">statusCode</span>);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">statusCode</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">statusCode</span> <span style="color:#f92672">!==</span> <span style="color:#ae81ff">200</span>;
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Blog awaken!&#39;</span>);  <span style="color:#75715e">// eslint-disable-line
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>И поставил его в крон на запуск каждую минуту.</p>
<pre tabindex="0"><code>* * * * * cd /home/vodolaz095/ &amp;&amp; node /home/vodolaz095/wakeBlog.js
</code></pre><p>Посмотрим, что из этого получится&hellip;</p>
<h1 id="обновление-2">Обновление 2<a href="#%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-2" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Скрипт для побудки блога проработал несколько лет. Но в 2026 году я решил делать личный сайт на <a href="https://gohugo.io/">Hugo</a>.
Поэтому, можно считать, что проблему я решил.</p>
]]></content></item><item><title>Документация о Javascript</title><link>https://vodolaz095.ru/javascript-links/</link><pubDate>Sat, 08 Jun 2019 02:06:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/javascript-links/</guid><description>Полезные ссылки на документацию по языку программирования Javascript</description><enclosure url="https://vodolaz095.ru/images/autumn_railway.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/autumn_railway.jpg" width="900" height="600"/><media:content url="https://vodolaz095.ru/images/autumn_railway.jpg" medium="image" type="image/jpeg" width="900" height="600"><media:title type="html">/images/autumn_railway.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/autumn_railway.jpg" alt="autumn_railway.jpg"></p>
<p>Полезные ссылки на документацию по языку программирования Javascript:</p>
<ol>
<li><a href="https://developer.mozilla.org/ru/">https://developer.mozilla.org/ru/</a></li>
<li><a href="https://javascript.ru">https://javascript.ru</a></li>
<li><a href="https://nodejs.org/api/">https://nodejs.org/api/</a></li>
</ol>
]]></content></item><item><title>Куров</title><link>https://vodolaz095.ru/kurov/</link><pubDate>Sat, 08 Jun 2019 02:06:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/kurov/</guid><description>ПРО́СТО Я АМГЫ́Л Просто я Вам говорил. Крайне неоднозначное выражение. По неопытности можно принять за оправдание.</description><enclosure url="https://vodolaz095.ru/images/kurov.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/kurov.jpg" width="245" height="309"/><media:content url="https://vodolaz095.ru/images/kurov.jpg" medium="image" type="image/jpeg" width="245" height="309"><media:title type="html">/images/kurov.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/kurov.jpg" alt="Андрей Владимирович Куров - фото из энтих ваших инторнетов."></p>
<p>Андрей Владимирович Куров - преподаватель из МГТУ имени Баумана с очень необычной манерой говорить.
Далее - попытка сделать перевод того, что он говорит.
Информация собрана из открытых источников.</p>
<ul>
<li><code>ЯГУ́/ Я ЖГУ</code>
Я говорю / Я же говорю. Обычно используется в начале или конце предложения, иногда не несёт смысловой нагрузки.</li>
<li><code>Я ГЫЛ</code> Я говорил. Обычно используется в середине предложения, иногда не несёт смысловой нагрузки.</li>
<li><code>ЮРИВТИ́ФЫЧЬ, ЮРДИ́ЧЬ, ЕВТИИ́ЧЬ, ЕБВЗДИ́ЧЬ, БВЗБВЗДИ́ЧЬ,  *****ДИ́ЧЬ.</code>
Преподаватель кафедры ИУ-7 Алексеев Юрий Евтихович. Варианты приведены в порядке возрастания
эмоциональности и уменьшения умственной активности. Последний вариант не поддаётся расшифровке.</li>
<li><code>КЛЫАТУ́РА</code> Клавиатура. Устройство ручного ввода информации в персональный компьютер.</li>
<li><code>КАА́́</code> Когда. Употребляется в начале предложения, иногда не несёт смысловой нагрузки.</li>
<li><code>ДА</code> Когда. Употребляется в конце предложения, иногда не несёт смысловой нагрузки. Бывает частью составных выражений вроде НУДА́, означающего &ldquo;ну когда&rdquo;.</li>
<li><code>В ОБЩА́ГЕ</code> Вообще говоря. Далее обычно следует очередь гласных звуков, количество которых свидетельствует об уровне умственного развития говорящего</li>
<li><code>ПАССА́ЛЬ</code> Pascal. Компилируемый язык программирования, изобретённый Никлаусом Виртом в 1970-м году. Используется в IDE Delphi. Изучается по программе первого курса кафедры ИУ-4 МГТУ имени Н.Э. Баумана.</li>
<li><code>ЙЕНТ</code> End. Оператор конца блока в языке Pascal.</li>
<li><code>ВЫ́НЮ!</code> Выгоню! Восклицание, применяемое в состоянии эмоционального возбуждения. Часто применяется не к месту.</li>
<li><code>CЪЕ́МА</code> Схема. Довольно умное слово.</li>
<li><code>АЛАРИ́ТУМ</code> Алгоритм. Последовательность действий, направленная на получение необходимого результата.</li>
<li><code>ПРОАГАИ́РОАНИЕ</code> Программирование. Процесс создания исходного кода программ, компилируемого в итоговое приложение.</li>
<li><code>ПШВО!</code> Пошёл вон! Обычно следует через несколько минут после слова, указанного в пункте №10.</li>
<li><code>ЩО</code> Ещё. Чаще всего встречается в словосочетании ШОЩО, обозначающем &ldquo;что ещё&rdquo;.</li>
<li><code>АА́ЧЕМ?</code> А зачем? Обычно используется в процессе завала студентов на лабораторных работах или экзаменах.</li>
<li><code>ИЗТОШО́</code> Из того, что. Крайне интеллектуальное выражение.</li>
<li><code>И́ЦА.</code>, <code>КАКЫ́ЦА.</code>,<code>НЕГЫ́ЦА.</code> Говорится, как говорится, не говорится. Вставляется в предложение тогда, когда мозг говорящего не может догнать до смысла говорённого.</li>
<li><code>АЧЁ́ЭТО ТАО́Е?</code> А что это такое? Обычно используется в процессе завала студентов на лабораторных работах или экзаменах.</li>
<li><code>ВЫ́ССИ ЗАО́БКУ!</code> Вынеси за скобку! Применяется тогда, когда преподаватель не хочет принимать лабораторную работу студента в настоящий момент.</li>
<li><code>НЕПЫА́ЙТЕ АДУ́ЙТЕ!</code> Не проверяйте, а думайте! Применяется в случае длительного поиска ошибок в лабораторной работы студентом.</li>
<li><code>АДЕ́ ТЫЭ́ТО ПОЧИТА́Л?</code> А где ты это прочитал? Обычно используется в процессе завала студентов на лабораторных работах или экзаменах.</li>
<li><code>ДУБА́ЙЕР.</code> Дебаггер. Средство отладки программ в IDE.</li>
<li><code>РИИТАТИ́Л.</code> Repeat until. Оператор цикла с постусловием в языке Pascal.</li>
<li><code>ПАГАА́МА</code>,<code>ПАПАГАА́МА</code> - Программа, подпрограмма.</li>
<li><code>СОСАМА́ИЦЫ</code>. Свойства матрицы. Употребляется чаще всего там, где матрицы нету вовсе.</li>
<li><code>АОМЕ́РНЫЙ</code>,<code>ДУМЕ́РНЫЙ</code>. Одномерный, Двумерный. Обычно используется применительно к массивам.</li>
<li><code>ГАДЕО́Б</code>. Гардероб. С наибольшей частотой произносится в первые 15 минут пары.</li>
<li><code>ССАСА́Л</code>. Сказал. Одно из немногих слов, всегда употребляемых там, где надо.</li>
<li><code>СУКИВШКА́Ф!</code> Сумки убрать в шкаф. Обычно находится в одном потоке слов вместе со словом <code>ГАДЕО́Б</code>.</li>
<li><code>НАЧТ</code>. Значит. Чаще всего обозначает начало очередного потока слов.</li>
<li><code>РАССО́ИМСЯ</code>. Расходимся. Последняя фраза на паре.</li>
<li><code>ФУРЫ́ЧЬ</code>. For each. Процедура языка Pascal для работы с множествами.</li>
<li><code>КАДВА́ТАЯ</code>. Квадратная. Чаще всего используется применительно к матрице.</li>
<li><code>БЫКА́ТАЯ</code>. <code>B[k]</code>. Обычно - про элемент множества.</li>
<li><code>ЛОХМА́ТАЯ МА́ИЦА</code>. Лохматая матрица. Матрица, у которой при выводе на экран в виде таблицы получается неровный правый край.</li>
<li><code>ДИНОЗА́УР ПОАГАИ́РОАНИЯ</code>. Динозавр программирования. Программист с опытом работы более 30 лет.</li>
<li><code>СТАРИЧКИ́</code>. Преподавательский состав МГТУ им. Н. Э. Баумана.</li>
<li><code>БОРМО́ЧУТ</code>. Преподают. Обычно используется применительно к СТАРИЧКАМ.</li>
<li><code>ДВО́ИШНИК</code>. Студент кафедры ИУ-4. Надо воспринимать не как характеристику, а как ссылку.</li>
<li><code>КАСТА́ТА</code>. Константа.</li>
<li><code>ПЬЕЭ́ННАЯ</code>. Переменная.</li>
<li><code>ВАРЕ́БЕЛ</code>. Variable.</li>
<li><code>МУ́ЙЕСТВО</code>. Множество. Тип данных, включающий в себя несколько однотипных элементов</li>
<li><code>НУА́ЙТЕ!</code> Ну давайте! Проявление снисходительности и/или доброй воли и/или побуждение к действию.</li>
<li><code>МЕТАБОЩА́</code>. Метод борща. Вольное переименование пузырькового метода сортировки.</li>
<li><code>КАКА́Т</code>. Concat. Функция языка Pascal для строковых переменных.</li>
<li><code>САКА́</code>,<code>ПАСАКА.</code> Строка, подстрока. Тип данных в языке Pascal.</li>
<li><code>РИСА́ЛЬТ</code> Result. Псевдопеременная для записи результата работы функции в языке Pascal.</li>
<li><code>КА́ЧАСТЕ</code> В качестве. Очень высокоинтеллектуальное выражение.</li>
<li><code>ПРО́СТО Я АМГЫ́Л</code> Просто я Вам говорил. Крайне неоднозначное выражение. По неопытности можно принять за оправдание.</li>
</ul>
]]></content></item><item><title>Список ссылок и литературы о языке Go.</title><link>https://vodolaz095.ru/golang-books/</link><pubDate>Sat, 08 Jun 2019 01:43:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/golang-books/</guid><description>Список ссылок и литературы о языке Go</description><enclosure url="https://vodolaz095.ru/images/cars.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/cars.jpg" width="900" height="600"/><media:content url="https://vodolaz095.ru/images/cars.jpg" medium="image" type="image/jpeg" width="900" height="600"><media:title type="html">/images/cars.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/cars.jpg" alt="cars.jpg"></p>
<p>Автомобили на шоссе - это аллегория вытесняющей многозадачности, реализованная в Go с помощью легковесных горутин.</p>
<h1 id="на-английском-языке">На Английском языке:<a href="#%d0%bd%d0%b0-%d0%b0%d0%bd%d0%b3%d0%bb%d0%b8%d0%b9%d1%81%d0%ba%d0%be%d0%bc-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<ol>
<li><a href="https://golang.org/">https://golang.org/</a> - официальное руководство</li>
<li><a href="https://www.golang-book.com/books/intro">https://www.golang-book.com/books/intro</a> - книга для начинающих от Калеба Доксли.</li>
<li><a href="https://gobyexample.com/">https://gobyexample.com/</a></li>
<li><a href="https://go.dev/">https://go.dev/</a></li>
</ol>
<h1 id="на-русском-языке">На Русском языке:<a href="#%d0%bd%d0%b0-%d1%80%d1%83%d1%81%d1%81%d0%ba%d0%be%d0%bc-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<ol>
<li><a href="http://golang-book.ru/">http://golang-book.ru/</a> - перевод книги Калеба Доксли</li>
<li><a href="https://www.ozon.ru/context/detail/id/148161581/">https://www.ozon.ru/context/detail/id/148161581/</a> - книга &ldquo;Язык программирования&rdquo; Go от Брайана Кернигана и Алана Донована.
Торренты ищите сами, книга стоит того, чтобы её купить.</li>
<li><a href="https://ru.wikipedia.org/wiki/Go">https://ru.wikipedia.org/wiki/Go</a></li>
</ol>
]]></content></item><item><title>Десертное мясо с мёдом</title><link>https://vodolaz095.ru/holymeat/</link><pubDate>Sat, 08 Jun 2019 00:47:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/holymeat/</guid><description>Мясо по рецепту наших предков.</description><content type="text/html" mode="escaped"><![CDATA[<p>Ингредиенты:</p>
<ol>
<li>Масло подсолнечное рафинированное 3.5 столовых ложки.</li>
<li>Мёд 1 столовая ложка.</li>
<li>Перец чёрный молотый 2 щепотки.</li>
<li>Мякоть &ldquo;свинины&rdquo; 400 г.</li>
<li>Половина чайной ложки соли.</li>
<li>Полторы чайных ложки столового уксуса.</li>
</ol>
<p>Приготовление:</p>
<ol>
<li>Мясо нарезать на кубики по 1 см</li>
<li>Мариновать 30 минут в холодильнике</li>
<li>Обжарить на чугунной сковороде около 30 минут, сначала на сильном огне, чтобы корочка образовалась, а потом на слабом огне, чтобы пропеклось.</li>
<li>Когда мясо подрумянится, то добавить оставшийся маринад.</li>
<li>В какой-то момент будет казаться, что мясо подгорает - но это иллюзия.</li>
<li>Через пару минут мясо начнёт действительно подгорать, и его надо снять с огня и перемешать.</li>
<li>Через пару минут на остаточном тепле мясо приготовится.</li>
</ol>
]]></content></item></channel></rss>