<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://sgeos.github.io/feed/freebsd.xml" rel="self" type="application/atom+xml" /><link href="https://sgeos.github.io/" rel="alternate" type="text/html" /><updated>2026-07-12T01:04:06+00:00</updated><id>https://sgeos.github.io/feed/freebsd.xml</id><title type="html">Brendan A R Sechter’s Development Blog | Freebsd</title><subtitle>A personal technical notebook covering systems programming, systems philosophy, tooling, mathematics, and emerging software paradigms.
</subtitle><author><name>Brendan Sechter</name></author><entry><title type="html">Installing SonarQube on FreeBSD</title><link href="https://sgeos.github.io/freebsd/sonarqube/2017/02/22/installing_sonarqube_on_freebsd.html" rel="alternate" type="text/html" title="Installing SonarQube on FreeBSD" /><published>2017-02-22T20:09:23+00:00</published><updated>2017-02-22T20:09:23+00:00</updated><id>https://sgeos.github.io/freebsd/sonarqube/2017/02/22/installing_sonarqube_on_freebsd</id><content type="html" xml:base="https://sgeos.github.io/freebsd/sonarqube/2017/02/22/installing_sonarqube_on_freebsd.html"><![CDATA[<!-- A52 -->
<script>console.log("A52");</script>

<p>SonarQube is a static analysis tool that supports many popular languages.
It can be integrated with tools like Jenkins and JIRA.
This post covers installing it on FreeBSD.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2017-02-22 20:09:23 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 11.0-RELEASE-p1 <span class="c">#0 r306420: Thu Sep 29 01:43:23 UTC 2016     root@releng2.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC  amd64</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<p>Install <strong>sonarqube</strong>.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>sonarqube</code></pre></figure>

<p>Create the database for <strong>sonarqube</strong>.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">SONARQUBE_USER</span><span class="o">=</span><span class="s2">"sonarqube"</span>
<span class="nv">SONARQUBE_DATABASE</span><span class="o">=</span><span class="s2">"sonarqube"</span>
<span class="nv">SONARQUBE_PASSWORD</span><span class="o">=</span><span class="s2">"secret.password"</span>

createuser <span class="s2">"</span><span class="k">${</span><span class="nv">SONARQUBE_USER</span><span class="k">}</span><span class="s2">"</span>
psql <span class="nt">-c</span> <span class="s2">"ALTER USER </span><span class="k">${</span><span class="nv">SONARQUBE_USER</span><span class="k">}</span><span class="s2"> WITH PASSWORD '</span><span class="k">${</span><span class="nv">SONARQUBE_PASSWORD</span><span class="k">}</span><span class="s2">';"</span>
createdb <span class="s2">"</span><span class="k">${</span><span class="nv">SONARQUBE_DATABASE</span><span class="k">}</span><span class="s2">"</span>
psql <span class="nt">-c</span> <span class="s2">"GRANT ALL PRIVILEGES ON DATABASE </span><span class="k">${</span><span class="nv">SONARQUBE_DATABASE</span><span class="k">}</span><span class="s2"> TO </span><span class="k">${</span><span class="nv">SONARQUBE_USER</span><span class="k">}</span><span class="s2">;"</span></code></pre></figure>

<p>Configure <strong>sonarqube</strong>.</p>

<p><strong>/usr/local/sonarqube/conf/sonar.properties</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#--------------------------------------------------------------------------------------------------</span>
<span class="c"># DATABASE</span>
<span class="c">#</span>
<span class="c"># IMPORTANT: the embedded H2 database is used by default. It is recommended for tests but not for</span>
<span class="c"># production use. Supported databases are MySQL, Oracle, PostgreSQL and Microsoft SQLServer.</span>

<span class="c"># User credentials.</span>
<span class="c"># Permissions to create tables, indices and triggers must be granted to JDBC user.</span>
<span class="c"># The schema must be created first.</span>
sonar.jdbc.username<span class="o">=</span>sonarqube
sonar.jdbc.password<span class="o">=</span>secret.password

<span class="c">#----- Embedded Database (default)</span>
<span class="c"># H2 embedded database server listening port, defaults to 9092</span>
<span class="c">#sonar.embeddedDatabase.port=9092</span>
<span class="c">#----- MySQL 5.6 or greater</span>
<span class="c"># Only InnoDB storage engine is supported (not myISAM).</span>
<span class="c"># Only the bundled driver is supported. It can not be changed.</span>
sonar.jdbc.url<span class="o">=</span>jdbc:postgresql://localhost/sonarqube</code></pre></figure>

<p>Enable <strong>sonarqube</strong>.</p>

<p><strong>/etc/rc.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">sonarqube_enable</span><span class="o">=</span><span class="s2">"YES"</span></code></pre></figure>

<p>Start <strong>sonarqube</strong>.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service sonarqube start</code></pre></figure>

<p>Alternatively, start the console by calling <strong>sonar.sh</strong> directly if you want verbose output.
This can be useful when troubleshooting.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">/usr/local/sonarqube/bin/freebsd/sonar.sh console</code></pre></figure>

<p>It takes a couple of minutes to start, but <strong>curl</strong> will return html if everything is working.
Alternatively, direct your browser to port 9000 of the server running <strong>sonarqube</strong>.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">curl localhost:9000</code></pre></figure>

<p><strong>SonarQube</strong> can be configured from the browser.</p>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://www.sonarqube.org">SonarQube</a></li>
  <li><a href="https://docs.sonarqube.org/display/HOME/SonarQube+Platform">SonarQube, Documentation</a></li>
  <li><a href="https://github.com/SonarSource/sonarqube">SonarQube, GitHub</a></li>
  <li><a href="https://hub.docker.com/_/sonarqube/">SonarQube, DockerHub</a></li>
  <li><a href="https://www.freshports.org/devel/sonarqube/">SonarQube, FreshPorts</a></li>
  <li><a href="http://macoscope.com/blog/using-sonarqube-with-jenkins-continuous-integration-and-github-to-improve-code-review/">SonarQube, Using SonarQube with Jenkins Continuous Integration and GitHub to Improve Code Review</a></li>
  <li><a href="http://linoxide.com/linux-how-to/install-sonarqube-ubuntu-16-04-ngnix/">SonarQube, How to Install SonarQube with Nginx on Ubuntu 16.04</a></li>
  <li><a href="http://stackoverflow.com/questions/30778850/sonarqube-with-postgres-db">SonarQube, SonarQube with Postgres DB</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="freebsd" /><category term="sonarqube" /></entry><entry><title type="html">Straightforward Shell Script Command Line Argument Parsing</title><link href="https://sgeos.github.io/sh/freebsd/2017/01/25/straightforward_shell_script_command_line_argument_parsing.html" rel="alternate" type="text/html" title="Straightforward Shell Script Command Line Argument Parsing" /><published>2017-01-25T14:27:00+00:00</published><updated>2017-01-25T14:27:00+00:00</updated><id>https://sgeos.github.io/sh/freebsd/2017/01/25/straightforward_shell_script_command_line_argument_parsing</id><content type="html" xml:base="https://sgeos.github.io/sh/freebsd/2017/01/25/straightforward_shell_script_command_line_argument_parsing.html"><![CDATA[<!-- A50 -->
<script>console.log("A50");</script>

<p>This post covers a relatively straightforward method of parsing commandline arguments.
All commandline arguments take the form of <strong>-p=value</strong> or <strong>–parameter=value</strong>.
A safe delimeter is used internally so the value can contain an equals sign.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2017-01-25 14:27:00 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 12.0-CURRENT <span class="c">#18 4f888bf(drm-next): Wed Jan 18 14:31:26 UTC 2017     root@gauntlet:/usr/obj/usr/src/sys/GENERIC  amd64</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<p>The basic strategy looks like this, with a safe delimiter.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">parse_args<span class="o">()</span> <span class="o">{</span>
  <span class="nv">SAFE_DELIMITER</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">printf</span> <span class="s2">"</span><span class="se">\a</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="k">while</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">""</span> <span class="o">]</span>
  <span class="k">do
    </span><span class="nv">PARAM</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$1</span> | <span class="nb">cut</span> <span class="nt">-f1</span> <span class="nt">-d</span><span class="o">=</span><span class="si">)</span>
    <span class="nv">VALUE</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$1</span> | <span class="nb">sed</span> <span class="s2">"s/=/</span><span class="k">${</span><span class="nv">SAFE_DELIMITER</span><span class="k">}</span><span class="s2">/"</span> | <span class="nb">cut</span> <span class="nt">-f2</span> <span class="s2">"-d</span><span class="k">${</span><span class="nv">SAFE_DELIMITER</span><span class="k">}</span><span class="s2">"</span><span class="si">)</span>

    <span class="k">case</span> <span class="nv">$PARAM</span> <span class="k">in</span>
      <span class="nt">-h</span> <span class="p">|</span> <span class="nt">--help</span><span class="p">)</span>
        usage
        <span class="nb">exit</span>
        <span class="p">;;</span>
      <span class="nt">-v</span> <span class="p">|</span> <span class="nt">--variable</span><span class="p">)</span>
        <span class="nv">G_VARIABLE</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">VALUE</span><span class="k">}</span><span class="s2">"</span>
        <span class="p">;;</span>
      <span class="k">*</span><span class="p">)</span>
        <span class="nb">echo</span> <span class="s2">"ERROR: Unknown parameter </span><span class="k">${</span><span class="nv">PARAM</span><span class="k">}</span><span class="s2">"</span>
        usage
        <span class="nb">exit </span>1
        <span class="p">;;</span>
    <span class="k">esac</span>
    <span class="nb">shift
  </span><span class="k">done</span>
<span class="o">}</span></code></pre></figure>

<p>A complete template example looks something like this.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>

set_defaults<span class="o">()</span> <span class="o">{</span>
  <span class="nv">G_URL</span><span class="o">=</span><span class="s2">"https://www.google.com"</span>
  <span class="nv">G_CREDENTIALS</span><span class="o">=</span><span class="s2">"username:password"</span>
<span class="o">}</span>

usage<span class="o">()</span> <span class="o">{</span>
  set_defaults
  <span class="nb">cat</span> <span class="o">&lt;&lt;-</span><span class="no">EOF</span><span class="sh">
./param.sh
Parameters:
  -h | --help:
    Show this help.
  -u | --url)
    URL
    Default: </span><span class="k">${</span><span class="nv">G_URL</span><span class="k">}</span><span class="sh">
  -l=username:password | --login=username:password)
    Username and password login credentials.
    Default: </span><span class="k">${</span><span class="nv">G_CREDENTIALS</span><span class="k">}</span><span class="sh">
</span><span class="no">EOF
</span><span class="o">}</span>

parse_args<span class="o">()</span> <span class="o">{</span>
  set_defaults
  <span class="nv">SAFE_DELIMITER</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">printf</span> <span class="s2">"</span><span class="se">\a</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="k">while</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">""</span> <span class="o">]</span>
  <span class="k">do
    </span><span class="nv">PARAM</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$1</span> | <span class="nb">cut</span> <span class="nt">-f1</span> <span class="nt">-d</span><span class="o">=</span><span class="si">)</span>
    <span class="nv">VALUE</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$1</span> | <span class="nb">sed</span> <span class="s2">"s/=/</span><span class="k">${</span><span class="nv">SAFE_DELIMITER</span><span class="k">}</span><span class="s2">/"</span> | <span class="nb">cut</span> <span class="nt">-f2</span> <span class="s2">"-d</span><span class="k">${</span><span class="nv">SAFE_DELIMITER</span><span class="k">}</span><span class="s2">"</span><span class="si">)</span>

    <span class="k">case</span> <span class="nv">$PARAM</span> <span class="k">in</span>
      <span class="nt">-h</span> <span class="p">|</span> <span class="nt">--help</span><span class="p">)</span>
        usage
        <span class="nb">exit</span>
        <span class="p">;;</span>
      <span class="nt">-u</span> <span class="p">|</span> <span class="nt">--url</span><span class="p">)</span>
        <span class="nv">G_URL</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">VALUE</span><span class="k">}</span><span class="s2">"</span>
        <span class="p">;;</span>
      <span class="nt">-l</span> <span class="p">|</span> <span class="nt">--login</span><span class="p">)</span>
        <span class="nv">G_CREDENTIALS</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">VALUE</span><span class="k">}</span><span class="s2">"</span>
        <span class="p">;;</span>
      <span class="k">*</span><span class="p">)</span>
        <span class="nb">echo</span> <span class="s2">"ERROR: Unknown parameter </span><span class="k">${</span><span class="nv">PARAM</span><span class="k">}</span><span class="s2">"</span>
        usage
        <span class="nb">exit </span>1
        <span class="p">;;</span>
    <span class="k">esac</span>
    <span class="nb">shift
  </span><span class="k">done</span>
<span class="o">}</span>

main<span class="o">()</span> <span class="o">{</span>
  parse_args <span class="nv">$@</span>
  <span class="nb">echo</span> <span class="s2">"URL=</span><span class="k">${</span><span class="nv">G_URL</span><span class="k">}</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"CREDENTIALS=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">G_CREDENTIALS</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">sed</span> <span class="s1">'s/:.*/:********/'</span><span class="si">)</span><span class="s2">"</span>
<span class="o">}</span>

main <span class="nv">$@</span></code></pre></figure>]]></content><author><name>Brendan Sechter</name></author><category term="sh" /><category term="freebsd" /></entry><entry><title type="html">Counting Files of a Particular Type from the Command Line</title><link href="https://sgeos.github.io/sh/freebsd/2017/01/25/counting_files_of_a_particular_type_from_the_command_line.html" rel="alternate" type="text/html" title="Counting Files of a Particular Type from the Command Line" /><published>2017-01-25T09:46:14+00:00</published><updated>2017-01-25T09:46:14+00:00</updated><id>https://sgeos.github.io/sh/freebsd/2017/01/25/counting_files_of_a_particular_type_from_the_command_line</id><content type="html" xml:base="https://sgeos.github.io/sh/freebsd/2017/01/25/counting_files_of_a_particular_type_from_the_command_line.html"><![CDATA[<!-- A49 -->
<script>console.log("A49");</script>

<p>Sometimes it can be useful to know how many files of a particular type are in a directory.
This post presents a single line command and a shell function that count the number of files with a given extenstion in the current directory.
Directories are searched recursively.
A version of the shell function in a stand alone script is also presented.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2017-01-25 09:46:14 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 12.0-CURRENT <span class="c">#18 4f888bf(drm-next): Wed Jan 18 14:31:26 UTC 2017     root@gauntlet:/usr/obj/usr/src/sys/GENERIC  amd64</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<p>The basic command to recursively tally files with a given extension looks like this.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">EXTENSION</span><span class="o">=</span>.sh
<span class="nb">ls</span> <span class="nt">-lR</span> | <span class="nb">grep</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXTENSION</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">wc</span> <span class="nt">-l</span></code></pre></figure>

<p>The can be used as the basis of a shell script.</p>

<p><strong>countfiles.sh</strong> complete listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>

countfiles<span class="o">()</span> <span class="o">{</span>
  <span class="k">for </span>extension <span class="k">in</span> <span class="nv">$@</span>
  <span class="k">do
    </span><span class="nv">count</span><span class="o">=</span><span class="si">$(</span><span class="nb">ls</span> <span class="nt">-lR</span> | <span class="nb">grep</span> <span class="s2">"</span><span class="k">${</span><span class="nv">extension</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
    <span class="nb">printf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">extension</span><span class="k">}</span><span class="se">\t</span><span class="k">${</span><span class="nv">count</span><span class="k">}</span><span class="se">\n</span><span class="s2">"</span>
  <span class="k">done</span>
<span class="o">}</span>

countfiles <span class="nv">$@</span></code></pre></figure>

<p>The shell script can be used as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">chmod</span> +x countfiles.sh
./countfiles.sh .java .kt .sh</code></pre></figure>

<p>Output will look something like this.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">.java	     892
.kt	       2
.sh	     251</code></pre></figure>

<p>Obviously, the countfiles function can be entered in the shell or <strong>.profile</strong>.
In that case it can be used as follows.</p>

<p><strong>.profile</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">countfiles<span class="o">()</span> <span class="o">{</span>
  <span class="k">for </span>extension <span class="k">in</span> <span class="nv">$@</span>
  <span class="k">do
    </span><span class="nv">count</span><span class="o">=</span><span class="si">$(</span><span class="nb">ls</span> <span class="nt">-lR</span> | <span class="nb">grep</span> <span class="s2">"</span><span class="k">${</span><span class="nv">extension</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
    <span class="nb">printf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">extension</span><span class="k">}</span><span class="se">\t</span><span class="k">${</span><span class="nv">count</span><span class="k">}</span><span class="se">\n</span><span class="s2">"</span>
  <span class="k">done</span>
<span class="o">}</span></code></pre></figure>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">countfiles.sh .java .kt .sh</code></pre></figure>

<p>Obviously, the output is exactly the same as above.</p>]]></content><author><name>Brendan Sechter</name></author><category term="sh" /><category term="freebsd" /></entry><entry><title type="html">Shell Script Confirmation Warning Prompt</title><link href="https://sgeos.github.io/sh/freebsd/2017/01/19/shell_script_warning_confirmation_prompt.html" rel="alternate" type="text/html" title="Shell Script Confirmation Warning Prompt" /><published>2017-01-19T12:21:09+00:00</published><updated>2017-01-19T12:21:09+00:00</updated><id>https://sgeos.github.io/sh/freebsd/2017/01/19/shell_script_warning_confirmation_prompt</id><content type="html" xml:base="https://sgeos.github.io/sh/freebsd/2017/01/19/shell_script_warning_confirmation_prompt.html"><![CDATA[<!-- A48 -->
<script>console.log("A48");</script>

<p>Sometimes scripts need to do something really destructive.
It is generally a good idea to display a warning prompt and provide a way to back out before performing these destructive actions.
It is also useful to provide a mechanism whereby the interactive prompt can be disabled.
This post covers one solution for this problem.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2017-01-19 12:21:09 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 12.0-CURRENT <span class="c">#14 f92c24b(drm-next-4.7): Fri Jan  6 19:28:21 UTC 2017     root@gauntlet:/usr/obj/usr/src/sys/GENERIC  amd64</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<p>The following script pretends to perform a destructive action when the <strong>NUKE_ENVIRONMENT</strong> environment variable is set to <strong>true</strong>.
By default, a prompt is displayed that allows the user to interactively proceed, skip the destructive action but execute the rest
of the script, or abort the script.</p>

<p>If the <strong>FORCE</strong> environment variable is also set to <strong>true</strong>, no prompt is displayed.
The script assumes the users knows what they are doing and executes the destructive action unconditionally.</p>

<p><strong>warn.sh</strong> complete listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>

<span class="c"># case insensitive settings</span>
<span class="nv">NUKE_ENVIRONMENT</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">NUKE_ENVIRONMENT</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">tr</span> <span class="s1">'[:upper:]'</span> <span class="s1">'[:lower:]'</span><span class="si">)</span>
<span class="nv">FORCE</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">FORCE</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">tr</span> <span class="s1">'[:upper:]'</span> <span class="s1">'[:lower:]'</span><span class="si">)</span>

<span class="c"># prompt if NUKE_ENVIRONMENT == true</span>
<span class="c"># skip prompt if FORCE == true</span>
<span class="k">if</span> <span class="o">[</span> <span class="se">\(</span> <span class="s2">"true"</span> <span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">NUKE_ENVIRONMENT</span><span class="k">}</span><span class="s2">"</span> <span class="se">\)</span> <span class="nt">-a</span> <span class="se">\(</span> <span class="s2">"true"</span> <span class="o">!=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">FORCE</span><span class="k">}</span><span class="s2">"</span> <span class="se">\)</span> <span class="o">]</span>
<span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Nuking the environment will do something really destructive."</span>
  <span class="nb">echo</span> <span class="s2">"This action can not be undone."</span>
  <span class="k">while </span><span class="nb">true
  </span><span class="k">do
    </span><span class="nb">read</span> <span class="nt">-p</span> <span class="s2">"Do you really want to nuke the environment? [yes/no/cancel] "</span> result
    <span class="k">case</span> <span class="nv">$result</span> <span class="k">in</span>
      <span class="o">[</span>Yy]<span class="k">*</span> <span class="p">)</span> <span class="nv">NUKE_ENVIRONMENT</span><span class="o">=</span><span class="s2">"true"</span><span class="p">;</span> <span class="nb">break</span><span class="p">;;</span>
      <span class="o">[</span>Nn]<span class="k">*</span> <span class="p">)</span> <span class="nv">NUKE_ENVIRONMENT</span><span class="o">=</span><span class="s2">"false"</span><span class="p">;</span> <span class="nb">break</span><span class="p">;;</span>
      <span class="o">[</span>Cc]<span class="k">*</span> <span class="p">)</span> <span class="nb">echo</span> <span class="s2">"Aborting script."</span><span class="p">;</span> <span class="nb">exit</span><span class="p">;;</span>
      <span class="k">*</span><span class="p">)</span> <span class="nb">echo</span> <span class="s2">"Please answer yes, no or cancel."</span><span class="p">;;</span>
    <span class="k">esac</span>
  <span class="k">done
fi</span>

<span class="c"># potentially nuke the environment</span>
<span class="k">if</span> <span class="o">[</span> <span class="s2">"true"</span> <span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">NUKE_ENVIRONMENT</span><span class="k">}</span><span class="s2">"</span> <span class="o">]</span>
<span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"* Do something really destructive here. *"</span>
  <span class="nb">echo</span> <span class="s2">"Nuking environment."</span>
<span class="k">else
  </span><span class="nb">echo</span> <span class="s2">"Skip nuking environment."</span>
<span class="k">fi</span>

<span class="c"># do everything else</span>
<span class="nb">echo</span> <span class="s2">"* Standard procedure here. *"</span></code></pre></figure>

<p>The script can be tested with the following commands.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">chmod</span> +x warn.sh
./warn.sh
<span class="nv">NUKE_ENVIRONMENT</span><span class="o">=</span><span class="nb">true</span> ./warn.sh
<span class="nv">NUKE_ENVIRONMENT</span><span class="o">=</span><span class="nb">true </span><span class="nv">FORCE</span><span class="o">=</span><span class="nb">true</span> ./warn.sh</code></pre></figure>

<p>This example uses environment variables directly.
A production example my use command line parameters instead.</p>]]></content><author><name>Brendan Sechter</name></author><category term="sh" /><category term="freebsd" /></entry><entry><title type="html">Printing the IP Address for a Device</title><link href="https://sgeos.github.io/sh/freebsd/unix/2017/01/17/printing_the_ip_address_for_a_device.html" rel="alternate" type="text/html" title="Printing the IP Address for a Device" /><published>2017-01-17T11:24:20+00:00</published><updated>2017-01-17T11:24:20+00:00</updated><id>https://sgeos.github.io/sh/freebsd/unix/2017/01/17/printing_the_ip_address_for_a_device</id><content type="html" xml:base="https://sgeos.github.io/sh/freebsd/unix/2017/01/17/printing_the_ip_address_for_a_device.html"><![CDATA[<!-- A47 -->
<script>console.log("A47");</script>

<p>I often work with VMs.
Sometimes I want to SSH into a physical server or a laptop.
Either way I need to know the IP address of the box to get work done.</p>

<p>This post covers a function that can be used to print the (DHCP) IP for a device on a box you are working on.
If you put it in <strong>.profile</strong>, you can log into a VM or physical box to get the IP address.
You can then use that to SSH into the box.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2017-01-17 11:24:20 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 12.0-CURRENT <span class="c">#14 f92c24b(drm-next-4.7): Fri Jan  6 19:28:21 UTC 2017     root@gauntlet:/usr/obj/usr/src/sys/GENERIC  amd64</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<p>Here is an <strong>sh</strong> function that can be used to print the ip address for a device.
I put it in <strong>.profile</strong>.</p>

<p><strong>.profile</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">print_ip_address<span class="o">(){</span>
  <span class="nv">ipv4</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>ifconfig <span class="s2">"</span><span class="k">${</span><span class="nv">1</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="s2">"inet "</span> | <span class="nb">sed</span> <span class="nt">-E</span> <span class="s2">"s/.*inet.([0-9.]*).*/</span><span class="se">\1</span><span class="s2">/"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nv">ipv6</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>ifconfig <span class="s2">"</span><span class="k">${</span><span class="nv">1</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="s2">"inet6 "</span> | <span class="nb">sed</span> <span class="nt">-E</span> <span class="s2">"s/.*inet6.([0-9a-f:]*).*/</span><span class="se">\1</span><span class="s2">/"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">printf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">1</span><span class="k">}</span><span class="se">\t</span><span class="k">${</span><span class="nv">ipv4</span><span class="k">:-</span><span class="p">(no ipv4)</span><span class="k">}</span><span class="se">\t</span><span class="k">${</span><span class="nv">ipv6</span><span class="k">:-</span><span class="p">(no ipv6)</span><span class="k">}</span><span class="se">\n</span><span class="s2">"</span>
<span class="o">}</span></code></pre></figure>

<p>I also have this at the bottom of <strong>.profile</strong>.</p>

<p><strong>.profile</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">hostname
</span>print_ip_address em0
print_ip_address wlan0</code></pre></figure>

<p>When I log in, I see something like this.</p>

<p><strong>sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">my.hostname.com
em0	<span class="o">(</span>no ipv4<span class="o">)</span>	fe80::3e97:eff:fe34:157c
wlan0	192.168.12.34	<span class="o">(</span>no ipv6<span class="o">)</span></code></pre></figure>

<p>A <strong>csh</strong> script (for FreeBSD root) that does the same thing looks something like this.</p>

<p><strong>print_ip.csh</strong> complete listing</p>

<figure class="highlight"><pre><code class="language-csh" data-lang="csh">#!/bin/csh

set device = $1
set ipv4 = `ifconfig "${device}" | grep "inet " | sed -E "s/.*inet.([0-9.]*).*/\1/" | sed 's/^$/(no ipv4)/'`
set ipv6 = `ifconfig "${device}" | grep "inet6 " | sed -E "s/.*inet6.([0-9a-f:]*).*/\1/" | sed 's/^$/(no ipv6)/'`
printf "${device}\t${ipv4}\t${ipv6}\n"</code></pre></figure>]]></content><author><name>Brendan Sechter</name></author><category term="sh" /><category term="freebsd" /><category term="unix" /></entry><entry><title type="html">Setting Up Lumina with slim in Japanese and Watching YouTube and DVDs on FreeBSD</title><link href="https://sgeos.github.io/freebsd/xorg/lumina/slim/chrome/vlc/2016/10/01/setting_up_lumina_with_slim_in_japanese_and_watching_youtube_and_dvds_on_freebsd.html" rel="alternate" type="text/html" title="Setting Up Lumina with slim in Japanese and Watching YouTube and DVDs on FreeBSD" /><published>2016-10-01T23:28:23+00:00</published><updated>2016-10-01T23:28:23+00:00</updated><id>https://sgeos.github.io/freebsd/xorg/lumina/slim/chrome/vlc/2016/10/01/setting_up_lumina_with_slim_in_japanese_and_watching_youtube_and_dvds_on_freebsd</id><content type="html" xml:base="https://sgeos.github.io/freebsd/xorg/lumina/slim/chrome/vlc/2016/10/01/setting_up_lumina_with_slim_in_japanese_and_watching_youtube_and_dvds_on_freebsd.html"><![CDATA[<!-- A46 -->
<script>console.log("A46");</script>

<p>This post covers installing Lumina and xdm on a fresh i386 install.
It also covers installing Japanese fonts and a Japanese IME.
The final test is watching DVDs and YouTube videos-
what a computer needs to do for my wife and kids to consider it useful.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2016-10-01 23:28:23 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 11.0-RC3 <span class="c">#0 r305786: Wed Sep 14 04:19:22 UTC 2016     root@releng2.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC  i386</span></code></pre></figure>

<h2 id="install-lumina-desktop-and-slim">Install Lumina Desktop and slim</h2>

<p>First, do a minimal install of FreeBSD and login as root.</p>

<p>Make your unprivileged user a member of the wheel group.
This will allow you to conveniently shutdown the computer.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pw groupmod wheel <span class="nt">-m</span> username</code></pre></figure>

<p><strong>NOTE:</strong> Consider increasing FETCH_TIMEOUT and/or FETCH_RETRY in
<strong>/usr/local/etc/pkg.conf</strong> if you are getting timeouts when installing packages.</p>

<p><strong>/usr/local/etc/pkg.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">FETCH_RETRY</span><span class="o">=</span>5
<span class="nv">FETCH_TIMEOUT</span><span class="o">=</span>90</code></pre></figure>

<p>I use <strong>vim</strong> to edit files, so that gets installed first.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>vim</code></pre></figure>

<p><strong>lumina</strong> and <strong>slim</strong> need to be installed.
The localized <strong>lumina-i18n</strong> pulls in <strong>lumina</strong> and <strong>xorg</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>lumina-i18n slim </code></pre></figure>

<p>Enable <strong>slim</strong> and <strong>dbus</strong> in <strong>/etc/rc.conf</strong>.
<strong>slim</strong> needs <strong>dbus</strong> to start Lumina.
<strong>hald</strong> is not strictly necessary, but it tends to be enabled when running xorg.
Also, if you use a mouse, make sure <strong>moused</strong> is enabled in <strong>/etc/rc.conf</strong>.</p>

<p><strong>/etc/rc.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># xorg</span>
<span class="nv">slim_enable</span><span class="o">=</span><span class="s2">"YES"</span>
<span class="nv">dbus_enable</span><span class="o">=</span><span class="s2">"YES"</span>
<span class="nv">hald_enable</span><span class="o">=</span><span class="s2">"YES"</span>
<span class="nv">moused_enable</span><span class="o">=</span><span class="s2">"YES"</span></code></pre></figure>

<p>Consider enabling <strong>sshd</strong> just in case something goes horribly wrong when configuring xorg.</p>

<p><strong>/etc/rc.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">sshd_enable</span><span class="o">=</span><span class="s2">"YES"</span></code></pre></figure>

<p>Generate <strong>xorg.conf</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">Xorg <span class="nt">-configure</span>
<span class="nb">cp</span> ~/xorg.conf.new /etc/X11/xorg.conf</code></pre></figure>

<p>Add the <code class="language-plaintext highlighter-rouge">Load "freetype"</code> to the “Module” section of <strong>/etc/X11/xorg.conf</strong>.</p>

<p><strong>/etc/X11/xorg.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">Section <span class="s2">"Module"</span>
        Load  <span class="s2">"glx"</span>
        Load  <span class="s2">"freetype"</span>
EndSection</code></pre></figure>

<p>Add the <code class="language-plaintext highlighter-rouge">FontPath "/usr/local/share/fonts/dejavu/"</code> to the “Files” section of <strong>/etc/X11/xorg.conf</strong>.</p>

<p><strong>/etc/X11/xorg.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">Section <span class="s2">"Files"</span>
        ModulePath   <span class="s2">"/usr/local/lib/xorg/modules"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/misc/"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/TTF/"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/OTF/"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/Type1/"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/100dpi/"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/75dpi/"</span>
        FontPath     <span class="s2">"/usr/local/share/fonts/dejavu/"</span>
EndSection</code></pre></figure>

<h2 id="japanese-fonts-and-ime">Japanese Fonts and IME</h2>

<p>Install the Japanese fonts and IME.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>japanese/font-std japanese/scim-anthy</code></pre></figure>

<p>Create the following <strong>.xinitrc</strong> for new users.</p>

<p><strong>/usr/share/skel/dot.xinitrc</strong> complete listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>

<span class="c"># set locale</span>
<span class="nb">export </span><span class="nv">LC_ALL</span><span class="o">=</span>ja_JP.UTF-8
<span class="nb">export </span><span class="nv">LANGUAGE</span><span class="o">=</span>ja_JP.UTF-8
<span class="nb">export </span><span class="nv">LANG</span><span class="o">=</span>ja_JP.UTF-8

<span class="c"># set input method</span>
<span class="nb">export </span><span class="nv">XMODIFIERS</span><span class="o">=</span><span class="s1">'@im=SCIM'</span>

<span class="c"># execute scim as a daemon</span>
scim <span class="nt">-d</span>

<span class="c"># key board layout</span>
setxkbmap <span class="nt">-layout</span> jp

<span class="c"># startx</span>
<span class="nb">exec</span> <span class="nv">$1</span></code></pre></figure>

<p>Copy the above <strong>.xinitrc</strong> to your home directory.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">USERNAME</span><span class="o">=</span>username
<span class="nb">cp</span> /usr/share/skel/dot.xinitrc /home/<span class="k">${</span><span class="nv">USERNAME</span><span class="k">}</span>/.xinitrc</code></pre></figure>

<p>After starting xorg, consider running <strong>scim-setup</strong> to configure <strong>scim</strong> for the IME.
By default, switch between kanji and romaji input with <em>Ctrl-[SPACE]</em>.</p>

<h2 id="youtube">YouTube</h2>

<p>As root, install <strong>chromium</strong> or your broswer of choice.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>chromium</code></pre></figure>

<p>As an unprivileged user start <strong>chrome</strong> and watch a <a href="https://www.youtube.com/watch?v=CynsucGqMtI">YouTube video</a>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">chrome https://www.youtube.com/watch?v<span class="o">=</span>CynsucGqMtI</code></pre></figure>

<h2 id="dvds">DVDs</h2>

<p>First, install <strong>vlc</strong> as root.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>vlc</code></pre></figure>

<p>Also install <strong>libdvdcss</strong>.
The following <em>should</em> work.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>libdvdcss</code></pre></figure>

<p>When I tried, the package was not available, so I had to build the port.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># fetch ports tree</span>
portsnap fetch extract

<span class="c"># install multimedia/libdvdcss port</span>
<span class="nb">cd</span> /usr/ports/multimedia/libdvdcss
make config recursive
make <span class="nb">install </span>clean</code></pre></figure>

<p>Add the following line to <strong>/etc/sysctl.conf</strong>.</p>

<p><strong>/etc/sysctl.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">vfs.usermount<span class="o">=</span>1</code></pre></figure>

<p>Verify the device name with <strong>camcontrol</strong>.
It is usually <strong>/dev/cd0</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">camcontrol devlist</code></pre></figure>

<p>Next, modify <strong>/etc/sysctl.conf</strong> to make the CDROM/DVDROM mountable by unprivileged users.
Use the device from the previous step.</p>

<p><strong>/etc/devfs.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">## Allow members of the operator to mount the cdrom</span>
own　　　　/dev/cd0    root:operator
perm　　　/dev/cd0     0660</code></pre></figure>

<p>Add your unprivileged user to the operator group.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pw groupmod operator <span class="nt">-m</span> username</code></pre></figure>

<p>Reboot.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">shutdown <span class="nt">-r</span> now</code></pre></figure>

<p>You should be able to mount and unmount the DVDs as your unprivileged user now.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">CDROM</span><span class="o">=</span>/dev/cd0
<span class="nb">mkdir</span> <span class="nt">-p</span> ~/media/cdrom
mount <span class="nt">-t</span> cd9660 <span class="s2">"</span><span class="k">${</span><span class="nv">CDROM</span><span class="k">}</span><span class="s2">"</span> ~/media/cdrom
umount ~/media/cdrom</code></pre></figure>

<p>As an your unprivileged user, insert a DVD and play it with VLC.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">CDROM</span><span class="o">=</span>/dev/cd0
vlc dvd://</code></pre></figure>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://www.freebsd.org/doc/handbook/x-xdm.html">FreeBSD Handbook, The X Display Manager</a></li>
  <li><a href="https://www.freebsd.org/doc/handbook/x-fonts.html">FreeBSD Handbook, Using Fonts in Xorg</a></li>
  <li><a href="https://www.freebsd.org/doc/handbook/desktop-browsers.html">FreeBSD Handbook, Browsers</a></li>
  <li><a href="https://www.freebsd.org/doc/handbook/video-playback.html">FreeBSD Handbook, Video Playback</a></li>
  <li><a href="http://freebsd.sing.ne.jp/desktop/08/01.html">FreeBSD, デスクトップ環境構築/Lumina/インストール</a></li>
  <li><a href="http://legacyos.ichmy.0t0.jp/virtualbsd/">FreeBSD で遊ぼうのこーな</a></li>
  <li><a href="http://silversack.my.coocan.jp/bsd/mate10x-buildmate.htm">FreeBSD, デスクトップ環境の構築(Mate 1.12.0 on FreeBSD 10</a></li>
  <li><a href="http://www.kishiro.com/FreeBSD/scim-anthy.html">FreeBSD, 日本語入力環境を構築する（scim/anthy）</a></li>
  <li><a href="http://d.hatena.ne.jp/hateua123/20121205/1354666208">FreeBSD, 日本語環境をscim-anthyで設定しました。</a></li>
  <li><a href="https://ryogan.org/blog/2012/12/08/freebsd-での-dvd-の再生やバックアップなど/">FreeBSD での DVD の再生やバックアップなど</a></li>
  <li><a href="https://forums.freebsd.org/threads/51501/">pkg, Operation timed out</a></li>
  <li><a href="https://wiki.videolan.org/Documentation:Command_line/#Opening_a_DVD_or_VCD.2C_or_an_audio_CD">VLC, Documentation:Command line</a></li>
  <li><a href="https://ubuntuforums.org/archive/index.php/t-1536685.html">VLC, DVD won’t play in VLC or MPlayer</a></li>
  <li><a href="https://ubuntuforums.org/archive/index.php/t-1536685.html">VLC, What are the file system of CD and DVD?</a></li>
  <li><a href="http://unix.stackexchange.com/questions/47359/what-is-xsession-for">xdm, What is “.xsession” for?</a></li>
  <li><a href="https://bugs.archlinux.org/task/13755">xdm, xorg-xdm uses the depreceated xsm as default session management</a></li>
  <li><a href="https://www.dragonflybsd.org/docs/how_to_get_to_the_desktop/">xorg, How to get a desktop on DragonFly</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="freebsd" /><category term="xorg" /><category term="lumina" /><category term="slim" /><category term="chrome" /><category term="vlc" /></entry><entry><title type="html">Installing MySQL on FreeBSD</title><link href="https://sgeos.github.io/mysql/freebsd/2016/07/14/installing-mysql-on-freebsd.html" rel="alternate" type="text/html" title="Installing MySQL on FreeBSD" /><published>2016-07-14T01:23:55+00:00</published><updated>2016-07-14T01:23:55+00:00</updated><id>https://sgeos.github.io/mysql/freebsd/2016/07/14/installing-mysql-on-freebsd</id><content type="html" xml:base="https://sgeos.github.io/mysql/freebsd/2016/07/14/installing-mysql-on-freebsd.html"><![CDATA[<!-- A37 -->
<script>console.log("A37");</script>

<p>Sometimes it can not be avoided.
MySQL needs to be installed for one reason or another.
This post covers installing MySQL on FreeBSD.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2016-07-14 01:23:55 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 11.0-ALPHA6 <span class="c">#0 r302384: Thu Jul  7 22:40:47 JST 2016     root@mirage.sennue.com:/usr/obj/usr/src/sys/MIRAGE_KERNEL  amd64</span>
<span class="nv">$ </span>mysqladmin <span class="nt">-u</span> root <span class="nt">-p</span> version
mysqladmin  Ver 8.42 Distrib 5.7.13, <span class="k">for </span>FreeBSD11.0 on amd64

Server version		5.7.13-log
Protocol version	10
<span class="k">*</span>snipped some output<span class="k">*</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<p>Install using packages.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg <span class="nb">install </span>mysql57-server mysql57-client</code></pre></figure>

<p>Alternatively, install using ports.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">portmaster databases/mysql57-server databases/mysql57-client</code></pre></figure>

<p>Enable MySQL in <strong>rc.conf</strong>.</p>

<p><strong>/etc/rc.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">mysql_enable</span><span class="o">=</span><span class="s2">"YES"</span></code></pre></figure>

<p>Start MySQL from the command line.
Note that the above <strong>rc.conf</strong> line will automatically start MySQL when
the machine boots.
Note that the default location for <strong>my.conf</strong> is
<strong>/usr/local/etc/mysql/my.cnf</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service mysql-server start</code></pre></figure>

<p>Note that if <strong>/var/db/mysql</strong> exists and is not empty,
<strong>mysql-server</strong> will fail to start the first time.
Backup or remove this directory if you need to.
See <a href="http://www.gamecreatures.com/blog/2016/02/22/freebsd-mysql-server-failed-precmd-routine/">this</a> blog post to track down other errors when starting
<strong>mysql-server</strong> for the first time.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># Backup</span>
<span class="nb">mv</span> /var/db/mysql /var/db/mysql.old

<span class="c"># Remove</span>
<span class="nb">rm</span> <span class="nt">-rf</span> /var/db/mysql</code></pre></figure>

<p>Reset the password.
Note that the initial password is stored in <strong>$HOME/.mysql_secret</strong>.
For example, if you use <code class="language-plaintext highlighter-rouge">mysql -u root -p</code> the initial password is stored in
<strong>/root/.mysql_secret</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mysql <span class="nt">-u</span> root <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';"</span></code></pre></figure>

<p>If upgrading from an earlier version, run <strong>mysql_upgrade</strong>.
Note that the command below upgrades as the root user.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mysql_upgrade <span class="nt">-u</span> root <span class="nt">-p</span></code></pre></figure>

<p>Uninstall with <strong>pkg</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">pkg delete mysql57-server mysql57-client</code></pre></figure>

<p>Uninstall with <strong>portmaster</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">portmaster <span class="nt">-e</span> databases/mysql57-server databases/mysql57-client</code></pre></figure>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://dev.mysql.com/doc/refman/5.7/en/freebsd-installation.html">MySQL, Installing MySQL on FreeBSD</a></li>
  <li><a href="http://dev.mysql.com/doc/refman/5.7/en/resetting-permissions.html">MySQL, How to Reset the Root Password</a></li>
  <li><a href="http://www.cyberciti.biz/faq/run-sql-query-directly-on-the-command-line/">MySQL, MySQL/MariaDB: Run SQL Queries From A Shell Prompt / Command Line</a></li>
  <li><a href="http://www.cyberciti.biz/faq/tell-version-mysql-unix-linux-command/">MySQL, Find Out MySQL Version</a></li>
  <li><a href="http://www.gamecreatures.com/blog/2016/02/22/freebsd-mysql-server-failed-precmd-routine/">FreeBSD, FreeBSD mysql-server failed precmd routine</a></li>
  <li><a href="https://forums.freebsd.org/threads/40833/">FreeBSD, Upgrade MySQL 5.1.70 to 5.6.12</a></li>
  <li><a href="https://forums.freebsd.org/threads/35457/">FreeBSD, Properly deinstall ports and dependencies</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="mysql" /><category term="freebsd" /></entry><entry><title type="html">Creating a Hidden Service with Phoenix and Elixir on FreeBSD</title><link href="https://sgeos.github.io/tor/freebsd/nc/curl/2016/03/22/creating-a-hidden-service-with-phoenix-and-elixir-on-freebsd.html" rel="alternate" type="text/html" title="Creating a Hidden Service with Phoenix and Elixir on FreeBSD" /><published>2016-03-22T06:45:48+00:00</published><updated>2016-03-22T06:45:48+00:00</updated><id>https://sgeos.github.io/tor/freebsd/nc/curl/2016/03/22/creating-a-hidden-service-with-phoenix-and-elixir-on-freebsd</id><content type="html" xml:base="https://sgeos.github.io/tor/freebsd/nc/curl/2016/03/22/creating-a-hidden-service-with-phoenix-and-elixir-on-freebsd.html"><![CDATA[<!-- A30 -->
<script>console.log("A30");</script>

<p>This post is a synthesis of a couple of prior posts.</p>

<ul>
  <li><a href="https://sgeos.github.io/phoenix/elixir/erlang/freebsd/2016/03/21/phoenix-as-a-service-on-freebsd.html">Phoenix as a Service on FreeBSD</a></li>
  <li><a href="https://sgeos.github.io/tor/freebsd/nc/curl/2016/02/06/getting-started-with-tor-hidden-services-on-freebsd.html">Getting Started With tor Hidden Services on FreeBSD</a></li>
</ul>

<p>It covers configuration and deployment of simple Phoenix application as a <a href="https://www.torproject.org/index.html.en">tor</a> hidden service.
Important topics like Phoenix auth, and properly securing a hidden service are not covered.</p>

<p><strong>security/tor</strong> needs to be installed.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">portmaster security/tor</code></pre></figure>

<p>Phoenix, Elixir and PostgreSQL also need to <a href="https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd.html">be installed</a>.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2016-03-22 06:45:48 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 11.0-CURRENT <span class="c">#0 r287598: Thu Sep 10 14:45:48 JST 2015     root@:/usr/obj/usr/src/sys/MIRAGE_KERNEL  amd64</span>
<span class="nv">$ </span>tor <span class="nt">--version</span>
Tor version 0.2.7.6.
<span class="nv">$ </span>curl <span class="nt">--version</span>
curl 7.47.0 <span class="o">(</span>amd64-portbld-freebsd11.0<span class="o">)</span> libcurl/7.47.0 OpenSSL/1.0.2e zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets 
<span class="nv">$ </span>mix hex.info
Hex:    0.11.3
Elixir: 1.2.3
OTP:    18.2.4
<span class="k">*</span> snip <span class="k">*</span>
<span class="nv">$ </span>mix phoenix.new <span class="nt">-v</span>
Phoenix v1.1.4</code></pre></figure>

<h2 id="creating-a-sample-project">Creating a Sample Project</h2>

<p>Create a new Phoenix project.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.new phoenix_service <span class="nt">--no-brunch</span>
<span class="nb">cd </span>phoenix_service
mix ecto.create</code></pre></figure>

<p>The sample project will be a simple memo JSON API service.
A real service will almost certainly need some sort of authentication,
but that is not covered in this post.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.gen.json Memo memos title:string body:string</code></pre></figure>

<p>Revise the <strong>web/router.ex</strong> file.
The “/api” scope needs to be uncommented and the “/memos” route needs to be added.</p>

<p><strong>web/router.ex</strong></p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir"><span class="k">defmodule</span> <span class="no">PhoenixService</span><span class="o">.</span><span class="no">Router</span> <span class="k">do</span>
  <span class="kn">use</span> <span class="no">PhoenixService</span><span class="o">.</span><span class="no">Web</span><span class="p">,</span> <span class="ss">:router</span>

  <span class="n">pipeline</span> <span class="ss">:browser</span> <span class="k">do</span>
    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="p">[</span><span class="s2">"html"</span><span class="p">]</span>
    <span class="n">plug</span> <span class="ss">:fetch_session</span>
    <span class="n">plug</span> <span class="ss">:fetch_flash</span>
    <span class="n">plug</span> <span class="ss">:protect_from_forgery</span>
    <span class="n">plug</span> <span class="ss">:put_secure_browser_headers</span>
  <span class="k">end</span>

  <span class="n">pipeline</span> <span class="ss">:api</span> <span class="k">do</span>
    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="p">[</span><span class="s2">"json"</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="n">scope</span> <span class="s2">"/"</span><span class="p">,</span> <span class="no">PhoenixService</span> <span class="k">do</span>
    <span class="n">pipe_through</span> <span class="ss">:browser</span> <span class="c1"># Use the default browser stack</span>
  
    <span class="n">get</span> <span class="s2">"/"</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:index</span>
  <span class="k">end</span>

  <span class="n">scope</span> <span class="s2">"/api"</span><span class="p">,</span> <span class="no">PhoenixService</span> <span class="k">do</span>
    <span class="n">pipe_through</span> <span class="ss">:api</span>

    <span class="n">resources</span> <span class="s2">"/memos"</span><span class="p">,</span> <span class="no">MemoController</span><span class="p">,</span> <span class="ss">except:</span> <span class="p">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>Run the migration.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix ecto.migrate</code></pre></figure>

<p>Configure the app to only serve localhost (127.0.0.1) in <strong>config/prod.exs</strong>.
Also make sure server is set to true and a dynamic port configuration is used.</p>

<p><strong>config/prod.exs</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir"><span class="n">config</span> <span class="ss">:phoenix_service</span><span class="p">,</span> <span class="no">PhoenixService</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
  <span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">"PORT"</span><span class="p">},</span> <span class="ss">ip:</span> <span class="p">{</span><span class="mi">127</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">}],</span> <span class="c1"># serve localhost with dynamic port</span>
  <span class="ss">url:</span> <span class="p">[</span><span class="ss">host:</span> <span class="s2">"example.com"</span><span class="p">,</span> <span class="ss">port:</span> <span class="mi">80</span><span class="p">],</span>
  <span class="ss">cache_static_manifest:</span> <span class="s2">"priv/static/manifest.json"</span><span class="p">,</span> <span class="c1"># added comma</span>
  <span class="ss">server:</span> <span class="no">true</span> <span class="c1"># this line is new</span></code></pre></figure>

<p>Optionally, configure the IP address and add a dynamic port configuration
with a default value to <strong>config/dev.exs</strong>.
The above dynamic port solution is suitable for releases.
The interpreted solution below is suitable for development.</p>

<p><strong>config/dev.exs</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir">  <span class="c1"># http: [port: 4000], # old line 10</span>
  <span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">"PORT"</span><span class="p">)</span> <span class="o">||</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:phoenix_service</span><span class="p">,</span> <span class="ss">:port</span><span class="p">)</span> <span class="o">||</span> <span class="mi">4000</span><span class="p">,</span> <span class="ss">ip:</span> <span class="p">{</span><span class="mi">127</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">}],</span></code></pre></figure>

<p>Tests should pass</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix <span class="nb">test</span></code></pre></figure>

<p>Start the server.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.server</code></pre></figure>

<p>POST and GET a memo to make sure the server works.
A <a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">prior post</a> covers a shell script for conveniently interacting with this sample app.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># POST new</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "New Title", "body": "This is the new memo body."}}'</span> http://localhost:4000/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://localhost:4000/api/memos/1</code></pre></figure>

<h2 id="generating-a-release">Generating a Release</h2>

<p>Now that the Phoenix app is working, it is time to build a release.
Add the <a href="https://github.com/bitwalker/exrm">elixir release manager (exrm)</a> to <strong>mix.exs</strong> as a project dependency.</p>

<p><strong>mix.exs</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir">  <span class="k">defp</span> <span class="n">deps</span> <span class="k">do</span>
    <span class="p">[{</span><span class="ss">:phoenix</span><span class="p">,</span> <span class="s2">"~&gt; 1.1.4"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:postgrex</span><span class="p">,</span> <span class="s2">"&gt;= 0.0.0"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:phoenix_ecto</span><span class="p">,</span> <span class="s2">"~&gt; 2.0"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:phoenix_html</span><span class="p">,</span> <span class="s2">"~&gt; 2.4"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:phoenix_live_reload</span><span class="p">,</span> <span class="s2">"~&gt; 1.0"</span><span class="p">,</span> <span class="ss">only:</span> <span class="ss">:dev</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:gettext</span><span class="p">,</span> <span class="s2">"~&gt; 0.9"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:exrm</span><span class="p">,</span> <span class="s2">"~&gt; 1.0.2"</span><span class="p">},</span> <span class="c1"># this line is new</span>
     <span class="p">{</span><span class="ss">:cowboy</span><span class="p">,</span> <span class="s2">"~&gt; 1.0"</span><span class="p">}]</span>
  <span class="k">end</span></code></pre></figure>

<p>Install exrm and build a release.
This will create the <strong>rel/</strong> directory.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix deps.get
mix deps.compile
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix ecto.create
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix ecto.migrate
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix compile
<span class="c"># brunch build --production # if using brunch</span>
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix phoenix.digest
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix release</code></pre></figure>

<p>The rc script will use environment variable knobs to configure the app.
Note that the <strong>RELX_REPLACE_OS_VARS=true</strong> environment variable needs
to be defined to use environment variables for dynamic configuration.</p>

<p>The <strong>vm.args</strong> file is primarily used to configure the erlang VM.
It can also be used to define application configure parameters.
Application configuration parameters defined in this file can be
passed into the program as atoms or integers.
Note that the location of this file can be <a href="https://exrm.readme.io/docs/release-configuration">configured</a>
with the <strong>RELEASE_CONFIG_DIR</strong> environment variable.
Add the following to <strong>rel/vm.args</strong>.</p>

<p><strong>rel/vm.args</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">## Name of the node</span>
<span class="nt">-name</span> <span class="k">${</span><span class="nv">NODE_NAME</span><span class="k">}</span>

<span class="c">## Cookie for distributed erlang</span>
<span class="nt">-setcookie</span> <span class="k">${</span><span class="nv">COOKIE</span><span class="k">}</span>

<span class="c">## App Settings</span>
<span class="nt">-phoenix_service</span> port <span class="k">${</span><span class="nv">PORT</span><span class="k">}</span></code></pre></figure>

<p>Alternatively, <strong>sys.config</strong> can be used to pass in
application configuration parameters.
In this file, application configuration parameters
defined with environment variables must be strings.
Pass the port setting in as above or add the following
to <strong>rel/sys.config</strong>.
The app module should work with either solution.
Adding both files will not break anything.
Note that <strong>rel/sys.config</strong> is written in Erlang.</p>

<p><strong>rel/sys.config</strong></p>

<figure class="highlight"><pre><code class="language-erlang" data-lang="erlang"><span class="p">[</span>
  <span class="p">{</span><span class="n">phoenix_service</span><span class="p">,</span> <span class="p">[</span>
    <span class="p">{</span><span class="n">port</span><span class="p">,</span> <span class="s">"${PORT}"</span><span class="p">}</span>
  <span class="p">]}</span>
<span class="p">].</span></code></pre></figure>

<p>Rebuild the release with the configuration files.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">MIX_ENV</span><span class="o">=</span>prod mix release</code></pre></figure>

<p>Start the release in the console.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">RELX_REPLACE_OS_VARS</span><span class="o">=</span><span class="nb">true </span><span class="nv">PORT</span><span class="o">=</span>7777 rel/phoenix_service/bin/phoenix_service console</code></pre></figure>

<p>Make sure the server responds.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">curl http://localhost:7777/api/memos</code></pre></figure>

<p>Exit the console with ^C.</p>

<p>Consider <a href="https://sgeos.github.io/phoenix/elixir/erlang/freebsd/2016/03/21/phoenix-as-a-service-on-freebsd.html">configuring the Phoenix app as a service</a>
to get it to start automatically when the machine boots.</p>

<h2 id="serving-a-hidden-service">Serving a Hidden Service</h2>

<p>Content needs to be served to a port on localhost.
This post will use port 8080.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">RELX_REPLACE_OS_VARS</span><span class="o">=</span><span class="nb">true </span><span class="nv">PORT</span><span class="o">=</span>8080 rel/phoenix_service/bin/phoenix_service start</code></pre></figure>

<p>Read the <a href="https://www.torproject.org/docs/tor-hidden-service.html.en">tor configuration instructions</a>.
Open <strong>/usr/local/etc/tor/torrc</strong> (see <a href="https://www.torproject.org/docs/faq.html.en#torrc">torrc instructions</a>).
Add the following lines to the section titled
“This section is just for location-hidden services”.</p>

<p><strong>/usr/local/etc/tor/torrc</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">HiddenServiceDir /usr/home/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:8080</code></pre></figure>

<p>Enable <strong>tor</strong> in <strong>/etc/rc.conf</strong></p>

<p><strong>/etc/rc.conf</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">tor_enable</span><span class="o">=</span><span class="s2">"YES"</span></code></pre></figure>

<p>Start <strong>tor</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service tor start</code></pre></figure>

<p>Get the hostname for your hidden service with the following command.
Do not share the <strong>private_key</strong>, found in the same directory.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">cat</span> /usr/home/tor/hidden_service/hostname</code></pre></figure>

<p>Test your hidden service with <strong>curl</strong> by supplying the tor proxy with the -x option.
The -v flag gives verbose output.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># POST new</span>
curl <span class="nt">-v</span> <span class="nt">-x</span> socks5h://127.0.0.1:9050 <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "New Title", "body": "This is the new memo body."}}'</span> http://<span class="si">$(</span><span class="nb">cat</span> /usr/home/tor/hidden_service/hostname<span class="si">)</span>/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-v</span> <span class="nt">-x</span> socks5h://127.0.0.1:9050 <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://<span class="si">$(</span><span class="nb">cat</span> /usr/home/tor/hidden_service/hostname<span class="si">)</span>/api/memos/1</code></pre></figure>

<p>You can also test your hidden service with <a href="https://tor2web.org">Tor2web</a>.
For example, if your hidden service has a hostname of ABCDEFGHIJKLMNOP.onion,
go to https://ABCDEFGHIJKLMNOP.onion.to to view it in a web browser.</p>

<p>The author of this post could not get Tor2web <strong>curl</strong> commands to work with a Phoenix app.
Something like the following commands should theoretically work.
Note that Tor2web blocks the curl user agent, so the user agent is set to test instead.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># POST new</span>
curl <span class="nt">-v</span> <span class="nt">-A</span> <span class="nb">test</span> <span class="nt">-x</span> socks5h://127.0.0.1:9050 <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "New Title", "body": "This is the new memo body."}}'</span> http://<span class="si">$(</span><span class="nb">cat</span> /usr/home/tor/hidden_service/hostname<span class="si">)</span>.to/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-v</span> <span class="nt">-A</span> <span class="nb">test</span> <span class="nt">-x</span> socks5h://127.0.0.1:9050 <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://<span class="si">$(</span><span class="nb">cat</span> /usr/home/tor/hidden_service/hostname<span class="si">)</span>.to/api/memos/1</code></pre></figure>

<p>To disable <strong>tor</strong> when you no longer need to use it, stop it with the <strong>service</strong> command.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service tor stop</code></pre></figure>

<p>Then disable it in <strong>/etc/rc.conf</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">tor_enable</span><span class="o">=</span><span class="s2">"NO"</span></code></pre></figure>

<h2 id="a-sample-shell-script-for-working-with-hidden-phoenix-json-apis">A Sample Shell Script for Working With Hidden Phoenix JSON APIs</h2>

<p>This is a modified version of the script covered in
<a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">Phoenix, A Shell Script for Working with Phoenix JSON APIs</a>.
See that post for a description of the script.</p>

<p>Support for flags has been added.
The flags default to using the tor proxy.
The default host has been changed to automatically
pull in the hidden service hostname.</p>

<p><strong>tor_memo_api.sh</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>

reset<span class="o">()</span> <span class="o">{</span>
  <span class="nv">HOST</span><span class="o">=</span>http://<span class="si">$(</span><span class="nb">cat</span> /usr/home/tor/hidden_service/hostname<span class="si">)</span>
  <span class="nv">SCOPE</span><span class="o">=</span>api
  <span class="nv">ROUTE</span><span class="o">=</span>memos
  <span class="nv">METHOD</span><span class="o">=</span><span class="s2">"GET"</span>
  <span class="nv">FLAGS</span><span class="o">=</span><span class="s2">"-x socks5h://127.0.0.1:9050"</span>
  <span class="nv">HEADERS</span><span class="o">=</span><span class="s2">"Content-Type: application/json"</span>
  <span class="nv">ID</span><span class="o">=</span><span class="s2">""</span>
  <span class="nv">TITLE</span><span class="o">=</span><span class="s2">""</span>
  <span class="nv">BODY</span><span class="o">=</span><span class="s2">""</span>
<span class="o">}</span>

usage<span class="o">()</span> <span class="o">{</span>
  reset
  <span class="nb">echo</span> <span class="s2">"Usage:  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> [options]"</span>
  <span class="nb">echo</span> <span class="s2">"Options:"</span>
  <span class="nb">echo</span> <span class="s2">"  -o HOST : set URL host, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">HOST</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -s SCOPE : set URL scope, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">SCOPE</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -r ROUTE : set URL route, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">ROUTE</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -X METHOD : set HTTP method, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -f FLAGS : set flags passed to curl, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">FLAGS</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -H HEADERS : set HTTP headers, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">HEADERS</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -i ID : set memo id, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">ID</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -t TITLE : set memo title, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">TITLE</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -b BODY : set memo body, defaults to </span><span class="se">\"</span><span class="k">${</span><span class="nv">BODY</span><span class="k">}</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  -h : display this help"</span>
  <span class="nb">echo</span> <span class="s2">"Examples:"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X GET"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X GET -i 7"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X POST -t </span><span class="se">\"</span><span class="s2">Memo Title</span><span class="se">\"</span><span class="s2"> -b </span><span class="se">\"</span><span class="s2">Memo body here.</span><span class="se">\"</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X PATCH -t </span><span class="se">\"</span><span class="s2">Patched title.</span><span class="se">\"</span><span class="s2"> -i 7"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X PATCH -b </span><span class="se">\"</span><span class="s2">Patched body.</span><span class="se">\"</span><span class="s2"> -i 7"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X PUT -t </span><span class="se">\"</span><span class="s2">New Title</span><span class="se">\"</span><span class="s2"> -b </span><span class="se">\"</span><span class="s2">New body.</span><span class="se">\"</span><span class="s2"> -i 7"</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="k">${</span><span class="nv">0</span><span class="k">}</span><span class="s2"> -X DELETE -i 7"</span>
  <span class="nb">exit</span> <span class="k">${</span><span class="nv">1</span><span class="k">}</span>
<span class="o">}</span>

reset
<span class="k">while </span><span class="nb">getopts</span> <span class="s2">"o:s:r:X:f:H:i:t:b:h"</span> opt
<span class="k">do
  case</span> <span class="s2">"</span><span class="k">${</span><span class="nv">opt</span><span class="k">}</span><span class="s2">"</span> <span class="k">in
    </span>o<span class="p">)</span> <span class="nv">HOST</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    s<span class="p">)</span> <span class="nv">SCOPE</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    r<span class="p">)</span> <span class="nv">ROUTE</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    X<span class="p">)</span> <span class="nv">METHOD</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    f<span class="p">)</span> <span class="nv">FLAGS</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    H<span class="p">)</span> <span class="nv">HEADERS</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    i<span class="p">)</span> <span class="nv">ID</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    t<span class="p">)</span> <span class="nv">TITLE</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    b<span class="p">)</span> <span class="nv">BODY</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">OPTARG</span><span class="k">}</span><span class="s2">"</span> <span class="p">;;</span>
    h<span class="p">)</span> usage 1 <span class="p">;;</span>
    <span class="se">\?</span><span class="p">)</span> usage 2 <span class="p">;;</span>
  <span class="k">esac</span>
<span class="k">done
</span><span class="nb">shift</span> <span class="si">$(</span><span class="nb">expr</span> <span class="k">${</span><span class="nv">OPTIND</span><span class="k">}</span> - 1<span class="si">)</span>

<span class="k">case</span> <span class="s2">"</span><span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span><span class="s2">"</span> <span class="k">in
  </span>GET<span class="p">)</span>
    curl <span class="k">${</span><span class="nv">FLAGS</span><span class="k">}</span> <span class="nt">-H</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HEADERS</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-X</span> <span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOST</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">SCOPE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ROUTE</span><span class="k">}${</span><span class="nv">ID</span>:+<span class="s2">"/</span><span class="k">${</span><span class="nv">ID</span><span class="k">}</span><span class="s2">"</span><span class="k">}</span><span class="s2">"</span>
    <span class="p">;;</span>
  POST<span class="p">)</span>
    <span class="nv">PAYLOAD</span><span class="o">=</span><span class="s1">'{"memo": {"title": "'</span><span class="s2">"</span><span class="k">${</span><span class="nv">TITLE</span><span class="k">:-</span><span class="p">(no title)</span><span class="k">}</span><span class="s2">"</span><span class="s1">'", "body": "'</span><span class="s2">"</span><span class="k">${</span><span class="nv">BODY</span><span class="k">:-</span><span class="p">(no body)</span><span class="k">}</span><span class="s2">"</span><span class="s1">'"}}'</span>
    curl <span class="k">${</span><span class="nv">FLAGS</span><span class="k">}</span> <span class="nt">-H</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HEADERS</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-X</span> <span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span> <span class="nt">-d</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PAYLOAD</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOST</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">SCOPE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ROUTE</span><span class="k">}</span><span class="s2">"</span> 
    <span class="p">;;</span>
  PUT<span class="p">)</span>
    <span class="nv">PAYLOAD</span><span class="o">=</span><span class="s1">'{"memo": {"title": "'</span><span class="s2">"</span><span class="k">${</span><span class="nv">TITLE</span><span class="k">:-</span><span class="p">(no title)</span><span class="k">}</span><span class="s2">"</span><span class="s1">'", "body": "'</span><span class="s2">"</span><span class="k">${</span><span class="nv">BODY</span><span class="k">:-</span><span class="p">(no body)</span><span class="k">}</span><span class="s2">"</span><span class="s1">'"}}'</span>
    curl <span class="k">${</span><span class="nv">FLAGS</span><span class="k">}</span> <span class="nt">-H</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HEADERS</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-X</span> <span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span> <span class="nt">-d</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PAYLOAD</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOST</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">SCOPE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ROUTE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ID</span>:?<span class="s1">'No ID specified.'</span><span class="k">}</span><span class="s2">"</span> 
    <span class="p">;;</span>
  PATCH<span class="p">)</span>
    <span class="c"># if defined replace individual fields with</span>
    <span class="c"># JSON fragments followed by a comma and space</span>
    <span class="nv">TITLE</span><span class="o">=</span><span class="k">${</span><span class="nv">TITLE</span>:+<span class="s2">"</span><span class="se">\"</span><span class="s2">title</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="k">${</span><span class="nv">TITLE</span><span class="k">}</span><span class="se">\"</span><span class="s2">, "</span><span class="k">}</span>
    <span class="nv">BODY</span><span class="o">=</span><span class="k">${</span><span class="nv">BODY</span>:+<span class="s2">"</span><span class="se">\"</span><span class="s2">body</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="k">${</span><span class="nv">BODY</span><span class="k">}</span><span class="se">\"</span><span class="s2">, "</span><span class="k">}</span>
    <span class="c"># strip trailing comma and space</span>
    <span class="nv">PAYLOAD</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TITLE</span><span class="k">}${</span><span class="nv">BODY</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">sed</span> <span class="s1">'s/, $//g'</span><span class="si">)</span><span class="s2">"</span>
    <span class="c"># complete JSON payload</span>
    <span class="nv">PAYLOAD</span><span class="o">=</span><span class="s2">"{</span><span class="se">\"</span><span class="s2">memo</span><span class="se">\"</span><span class="s2">: {</span><span class="k">${</span><span class="nv">PAYLOAD</span><span class="k">}</span><span class="s2">}}"</span>
    curl <span class="k">${</span><span class="nv">FLAGS</span><span class="k">}</span> <span class="nt">-H</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HEADERS</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-X</span> <span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span> <span class="nt">-d</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PAYLOAD</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOST</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">SCOPE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ROUTE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ID</span>:?<span class="s1">'No ID specified.'</span><span class="k">}</span><span class="s2">"</span> 
    <span class="p">;;</span>
  DELETE<span class="p">)</span>
    curl <span class="k">${</span><span class="nv">FLAGS</span><span class="k">}</span> <span class="nt">-H</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HEADERS</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-X</span> <span class="k">${</span><span class="nv">METHOD</span><span class="k">}</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOST</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">SCOPE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ROUTE</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">ID</span>:?<span class="s1">'No ID specified.'</span><span class="k">}</span><span class="s2">"</span>
    <span class="p">;;</span>
  <span class="k">*</span><span class="p">)</span>
    usage 2
    <span class="p">;;</span>
<span class="k">esac</span>
<span class="nb">echo</span> <span class="s2">""</span></code></pre></figure>

<p>As written, the above script can only be run as root or the tor
user because <strong>/usr/home/tor</strong> does not have global read permissions.
Changing the permissions is a bad idea.
Instead, hard code the default host if you want to
be able to use the script with unprivileged users.</p>

<p><strong>tor_memo_api.sh</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">HOST</span><span class="o">=</span><span class="s2">"http://abcdefghijklmnop.onion"</span></code></pre></figure>

<p>The script can be used as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">chmod</span> +x tor_memo_api.sh
./tor_memo_api.sh <span class="nt">-X</span> POST <span class="nt">-t</span> <span class="s2">"Memo Title"</span> <span class="nt">-b</span> <span class="s2">"Memo body here."</span>
./tor_memo_api.sh <span class="nt">-X</span> GET <span class="nt">-i</span> 1
./tor_memo_api.sh <span class="nt">-X</span> POST <span class="nt">-t</span> <span class="s2">"Another Memo"</span> <span class="nt">-b</span> <span class="s2">"This memo's body."</span>
./tor_memo_api.sh <span class="nt">-X</span> PATCH <span class="nt">-t</span> <span class="s2">"Patched title."</span> <span class="nt">-i</span> 2
./tor_memo_api.sh <span class="nt">-X</span> PATCH <span class="nt">-b</span> <span class="s2">"Patched body."</span> <span class="nt">-i</span> 1
./tor_memo_api.sh <span class="nt">-X</span> PUT <span class="nt">-t</span> <span class="s2">"New Title"</span> <span class="nt">-b</span> <span class="s2">"New body."</span> <span class="nt">-i</span> 2
./tor_memo_api.sh <span class="nt">-X</span> GET
./tor_memo_api.sh <span class="nt">-X</span> DELETE <span class="nt">-i</span> 1</code></pre></figure>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://github.com/bitwalker/exrm">Elixir Release Manager</a></li>
  <li><a href="https://exrm.readme.io/docs/release-configuration">Elixir, exrm, Release Configuration</a></li>
  <li><a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">Phoenix, A Shell Script for Working with Phoenix JSON APIs</a></li>
  <li><a href="https://www.torproject.org/index.html.en">Tor Project</a></li>
  <li><a href="https://tor2web.org">Tor, Tor2web</a></li>
  <li><a href="https://www.torproject.org/docs/tor-hidden-service.html.en">Tor, Configuring Hidden Services for Tor</a></li>
  <li><a href="https://www.torproject.org/docs/faq.html.en#torrc">Tor, Edit torrc</a></li>
  <li><a href="https://github.com/curl/curl/issues/232">Tor, HTTP connection closed on: Excess found in a non pipelined read</a></li>
  <li><a href="http://stackoverflow.com/questions/18146295/curl-an-onion-url-over-an-http-proxy-does-not-return-expected-source">Tor, StackOverflow, curl an .onion url over an http proxy does not return expected source</a></li>
  <li><a href="https://www.freebsd.org/cgi/man.cgi?query=curl&amp;manpath=SuSE+Linux/i386+11.3">FreeBSD, Man curl</a></li>
  <li><a href="https://sgeos.github.io/phoenix/elixir/erlang/freebsd/2016/03/21/phoenix-as-a-service-on-freebsd.html">FreeBSD, Phoenix as a Service on FreeBSD</a></li>
  <li><a href="https://sgeos.github.io/tor/freebsd/nc/curl/2016/02/06/getting-started-with-tor-hidden-services-on-freebsd.html">FreeBSD, Getting Started With tor Hidden Services on FreeBSD</a></li>
  <li><a href="https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd.html">FreeBSD, Installing Phoenix, Elixir and PostgreSQL on FreeBSD</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="tor" /><category term="freebsd" /><category term="nc" /><category term="curl" /></entry><entry><title type="html">Phoenix as a Service on FreeBSD</title><link href="https://sgeos.github.io/phoenix/elixir/erlang/freebsd/2016/03/21/phoenix-as-a-service-on-freebsd.html" rel="alternate" type="text/html" title="Phoenix as a Service on FreeBSD" /><published>2016-03-21T15:48:51+00:00</published><updated>2016-03-21T15:48:51+00:00</updated><id>https://sgeos.github.io/phoenix/elixir/erlang/freebsd/2016/03/21/phoenix-as-a-service-on-freebsd</id><content type="html" xml:base="https://sgeos.github.io/phoenix/elixir/erlang/freebsd/2016/03/21/phoenix-as-a-service-on-freebsd.html"><![CDATA[<!-- A29 -->
<script>console.log("A29");</script>

<p>In this post a Phoenix app will be installed as a service on FreeBSD.
The goals are the same as the <a href="https://sgeos.github.io/elixir/erlang/2016/01/16/elixir-as-a-service_on_freebsd.html">Elixir as a Service on FreeBSD post</a>.</p>

<ul>
  <li>The app needs to be able to be rebuilt and the service
restarted to reflect changes on a development machine.</li>
  <li>The service needs to automatically start when the machine is booted.</li>
  <li>The service sould work like any other service.</li>
</ul>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service phoenix_service start
service phoenix_service stop
service phoenix_service restart
service phoenix_service status</code></pre></figure>

<p>This post assumes Phoenix, Elixir and PostgreSQL are <a href="https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd.html">already installed on FreeBSD</a>.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2016-03-21 15:48:51 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 11.0-CURRENT <span class="c">#0 r296925: Wed Mar 16 20:53:04 JST 2016     root@mirage.sennue.com:/usr/obj/usr/src/sys/MIRAGE_KERNEL  amd64</span>
<span class="nv">$ </span>mix hex.info
Hex:    0.11.3
Elixir: 1.2.3
OTP:    18.2.4
<span class="k">*</span> snip <span class="k">*</span>
<span class="nv">$ </span>mix phoenix.new <span class="nt">-v</span>
Phoenix v1.1.4</code></pre></figure>

<h2 id="creating-a-sample-project">Creating a Sample Project</h2>

<p>Create a new Phoenix project.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.new phoenix_service <span class="nt">--no-brunch</span>
<span class="nb">cd </span>phoenix_service
mix ecto.create</code></pre></figure>

<p>This sample project needs to be simple.
Let’s make a JSON memo service.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.gen.json Memo memos title:string body:string</code></pre></figure>

<p>Revise the <strong>web/router.ex</strong> file.
The “/api” scope needs to be uncommented and the “/memos” route needs to be added.</p>

<p><strong>web/router.ex file</strong></p>

<figure class="highlight"><pre><code class="language-elixer" data-lang="elixer">defmodule PhoenixService.Router do
  use PhoenixService.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", PhoenixService do
    pipe_through :browser # Use the default browser stack
  
    get "/", PageController, :index
  end

  scope "/api", PhoenixService do
    pipe_through :api

    resources "/memos", MemoController, except: [:new, :edit]
  end
end</code></pre></figure>

<p>Run the migration.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix ecto.migrate</code></pre></figure>

<p>Set server to true in <strong>config/prod.exs</strong>.
Also make sure a dynamic port configuration is used.</p>

<p><strong>config/prod.exs</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir"><span class="n">config</span> <span class="ss">:phoenix_service</span><span class="p">,</span> <span class="no">PhoenixService</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
  <span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">"PORT"</span><span class="p">}],</span> <span class="c1"># dynamic port configuration</span>
  <span class="ss">url:</span> <span class="p">[</span><span class="ss">host:</span> <span class="s2">"example.com"</span><span class="p">,</span> <span class="ss">port:</span> <span class="mi">80</span><span class="p">],</span>
  <span class="ss">cache_static_manifest:</span> <span class="s2">"priv/static/manifest.json"</span><span class="p">,</span> <span class="c1"># added comma</span>
  <span class="ss">server:</span> <span class="no">true</span> <span class="c1"># this line is new</span></code></pre></figure>

<p>Optionally, add a dynamic port configuration with a default value to <strong>config/dev.exs</strong>.
The above build time solution is suitable for releases.
The interpreted solution below is suitable for development.</p>

<p><strong>config/dev.exs</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir">  <span class="c1"># http: [port: 4000], # old line 10</span>
  <span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">"PORT"</span><span class="p">)</span> <span class="o">||</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:phoenix_service</span><span class="p">,</span> <span class="ss">:port</span><span class="p">)</span> <span class="o">||</span> <span class="mi">4000</span><span class="p">],</span></code></pre></figure>

<p>Tests should pass</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix <span class="nb">test</span></code></pre></figure>

<p>Start the server.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.server</code></pre></figure>

<p>POST and GET a memo to make sure the server works.
A <a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">prior post</a> covers a shell script for conveniently
interacting with this sample app.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># POST new</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "New Title", "body": "This is the new memo body."}}'</span> http://localhost:4000/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://localhost:4000/api/memos/1</code></pre></figure>

<h2 id="generating-a-release">Generating a Release</h2>

<p>Now that the Phoenix app is working, it is time to build a release.
Add the <a href="https://github.com/bitwalker/exrm">elixir release manager (exrm)</a> to <strong>mix.exs</strong> as a project dependency.</p>

<p><strong>mix.exs</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-elixir" data-lang="elixir">  <span class="k">defp</span> <span class="n">deps</span> <span class="k">do</span>
    <span class="p">[{</span><span class="ss">:phoenix</span><span class="p">,</span> <span class="s2">"~&gt; 1.1.4"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:postgrex</span><span class="p">,</span> <span class="s2">"&gt;= 0.0.0"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:phoenix_ecto</span><span class="p">,</span> <span class="s2">"~&gt; 2.0"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:phoenix_html</span><span class="p">,</span> <span class="s2">"~&gt; 2.4"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:phoenix_live_reload</span><span class="p">,</span> <span class="s2">"~&gt; 1.0"</span><span class="p">,</span> <span class="ss">only:</span> <span class="ss">:dev</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:gettext</span><span class="p">,</span> <span class="s2">"~&gt; 0.9"</span><span class="p">},</span>
     <span class="p">{</span><span class="ss">:exrm</span><span class="p">,</span> <span class="s2">"~&gt; 1.0.2"</span><span class="p">},</span> <span class="c1"># this line is new</span>
     <span class="p">{</span><span class="ss">:cowboy</span><span class="p">,</span> <span class="s2">"~&gt; 1.0"</span><span class="p">}]</span>
  <span class="k">end</span></code></pre></figure>

<p>Install exrm and build a release.
This will create the <strong>rel/</strong> directory.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix deps.get
mix deps.compile
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix ecto.create
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix ecto.migrate
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix compile
<span class="c"># brunch build --production # if using brunch</span>
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix phoenix.digest
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix release</code></pre></figure>

<p>The rc script will use environment variable knobs to configure the app.
Note that the <strong>RELX_REPLACE_OS_VARS=true</strong> environment variable needs
to be defined to use environment variables for dynamic configuration.</p>

<p>The <strong>vm.args</strong> file is primarily used to configure the erlang VM.
It can also be used to define application configure parameters.
Application configuration parameters defined in this file can be
passed into the program as atoms or integers.
Note that the location of this file can be <a href="https://exrm.readme.io/docs/release-configuration">configured</a>
with the <strong>RELEASE_CONFIG_DIR</strong> environment variable.
Add the following to <strong>rel/vm.args</strong>.</p>

<p><strong>rel/vm.args</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">## Name of the node</span>
<span class="nt">-name</span> <span class="k">${</span><span class="nv">NODE_NAME</span><span class="k">}</span>

<span class="c">## Cookie for distributed erlang</span>
<span class="nt">-setcookie</span> <span class="k">${</span><span class="nv">COOKIE</span><span class="k">}</span>

<span class="c">## App Settings</span>
<span class="nt">-phoenix_service</span> port <span class="k">${</span><span class="nv">PORT</span><span class="k">}</span></code></pre></figure>

<p>Alternatively, <strong>sys.config</strong> can be used to pass in
application configuration parameters.
In this file, application configuration parameters
defined with environment variables must be strings.
Pass the port setting in as above or add the following
to <strong>rel/sys.config</strong>.
The app module should work with either solution.
Adding both files will not break anything.
Note that <strong>rel/sys.config</strong> is written in Erlang.</p>

<p><strong>rel/sys.config</strong></p>

<figure class="highlight"><pre><code class="language-erlang" data-lang="erlang"><span class="p">[</span>
  <span class="p">{</span><span class="n">phoenix_service</span><span class="p">,</span> <span class="p">[</span>
    <span class="p">{</span><span class="n">port</span><span class="p">,</span> <span class="s">"${PORT}"</span><span class="p">}</span>
  <span class="p">]}</span>
<span class="p">].</span></code></pre></figure>

<p>Rebuild the release with the configuration files.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">MIX_ENV</span><span class="o">=</span>prod mix release</code></pre></figure>

<p>Start the release in the console.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">RELX_REPLACE_OS_VARS</span><span class="o">=</span><span class="nb">true </span><span class="nv">PORT</span><span class="o">=</span>7777 rel/phoenix_service/bin/phoenix_service console</code></pre></figure>

<p>Make sure the server responds.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">curl http://localhost:7777/api/memos</code></pre></figure>

<p>Exit the console with ^C.</p>

<h2 id="installing-the-release-as-a-service">Installing the Release as a Service</h2>

<p>Now that the release is working, it is time to set it up as a service.
Perform the initial install.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">su
sh
<span class="nv">PROJECT</span><span class="o">=</span>phoenix_service
<span class="nv">INSTALL_DIR</span><span class="o">=</span>/usr/local/opt
<span class="nv">VERSION</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat </span>rel/<span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span>/releases/start_erl.data | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f2</span><span class="si">)</span>
<span class="nv">INSTALL_TAR</span><span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/rel/<span class="nv">$PROJECT</span>/releases/<span class="nv">$VERSION</span>/<span class="nv">$PROJECT</span>.tar.gz
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>
<span class="o">(</span><span class="nb">cd</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span><span class="p">;</span> <span class="nb">tar</span> <span class="nt">-xf</span> <span class="nv">$INSTALL_TAR</span><span class="o">)</span>
pw adduser <span class="nv">$PROJECT</span> <span class="nt">-d</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span> <span class="nt">-s</span> /usr/sbin/nologin <span class="nt">-c</span> <span class="s2">"</span><span class="nv">$PROJECT</span><span class="s2"> system service user"</span>
<span class="nb">chown</span> <span class="nt">-R</span> <span class="nv">$PROJECT</span>:<span class="nv">$PROJECT</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span></code></pre></figure>

<p>An rc script defines the the service.</p>

<ul>
  <li><strong>phoenix_service_run()</strong> is called from the other functions.
It configures and calls the release.
<strong>HOME</strong> is set to the installation directory to force the
erlang cookie file to be written there regardless of the
<strong>phoenix_service_user</strong> setting.</li>
  <li><strong>phoenix_service_status()</strong> echoes a user friendly
message if the release can be pinged.</li>
  <li><strong>extra_commands</strong> is used to add the <strong>console</strong> and
<strong>remote_console</strong> commands.
<strong>console</strong> is used to start a new service and attach a console to it.
<strong>remote_console()</strong> is used to connect a console to the service
if it is already running.</li>
  <li>Add <strong>shutdown</strong> to the keyword list if the service needs to
gracefull shutdown when the machine restarts.</li>
  <li>The rest is standard rc configuration.</li>
</ul>

<p>Add following to <strong>/usr/local/etc/rc.d/phoenix_service</strong></p>

<p><strong>/usr/local/etc/rc.d/phoenix_service</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>
<span class="c">#</span>
<span class="c"># PROVIDE: phoenix_service</span>
<span class="c"># REQUIRE: networking</span>
<span class="c"># KEYWORD:</span>
 
<span class="nb">.</span> /etc/rc.subr
 
<span class="nv">name</span><span class="o">=</span><span class="s2">"phoenix_service"</span>
<span class="nv">rcvar</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">_enable"</span>
<span class="nv">install_dir</span><span class="o">=</span><span class="s2">"/usr/local/opt/</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">version</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat</span> <span class="k">${</span><span class="nv">install_dir</span><span class="k">}</span>/releases/start_erl.data | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f2</span><span class="si">)</span>
<span class="nb">command</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">install_dir</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">"</span>
 
<span class="nv">start_cmd</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">_start"</span>
<span class="nv">stop_cmd</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">_stop"</span>
<span class="nv">status_cmd</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">_status"</span>
<span class="nv">console_cmd</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">_console"</span>
<span class="nv">remote_console_cmd</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">_remote_console"</span>
<span class="nv">extra_commands</span><span class="o">=</span><span class="s2">"console remote_console"</span>

load_rc_config <span class="nv">$name</span>
: <span class="k">${</span><span class="nv">phoenix_service_enable</span>:<span class="p">=</span><span class="s2">"no"</span><span class="k">}</span>
: <span class="k">${</span><span class="nv">phoenix_service_port</span>:<span class="p">=</span><span class="s2">"4000"</span><span class="k">}</span>
: <span class="k">${</span><span class="nv">phoenix_service_user</span>:<span class="p">=</span><span class="k">${</span><span class="nv">name</span><span class="k">}}</span>
: <span class="k">${</span><span class="nv">phoenix_service_node_name</span>:<span class="p">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">@127.0.0.1"</span><span class="k">}</span>
: <span class="k">${</span><span class="nv">phoenix_service_cookie</span>:<span class="p">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2">"</span><span class="k">}</span>
: <span class="k">${</span><span class="nv">phoenix_service_config_dir</span>:<span class="p">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">install_dir</span><span class="k">}</span><span class="s2">/releases/</span><span class="k">${</span><span class="nv">version</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2"> start"</span><span class="k">}</span>

phoenix_service_run<span class="o">()</span>
<span class="o">{</span>
  <span class="nv">RELX_REPLACE_OS_VARS</span><span class="o">=</span><span class="nb">true</span> <span class="se">\</span>
  <span class="nv">HOME</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">install_dir</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nv">RELEASE_CONFIG_DIR</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">phoenix_service_config</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nv">NODE_NAME</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">phoenix_service_node_name</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nv">COOKIE</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">phoenix_service_cookie</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nv">PORT</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">phoenix_service_port</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
  su <span class="nt">-m</span> <span class="s2">"</span><span class="nv">$phoenix_service_user</span><span class="s2">"</span> <span class="nt">-c</span> <span class="s2">"</span><span class="nv">$command</span><span class="s2"> </span><span class="nv">$1</span><span class="s2">"</span>
<span class="o">}</span>

phoenix_service_start<span class="o">()</span>
<span class="o">{</span>
  phoenix_service_run start
<span class="o">}</span>

phoenix_service_stop<span class="o">()</span>
<span class="o">{</span>
  phoenix_service_run stop
<span class="o">}</span>

phoenix_service_status<span class="o">()</span>
<span class="o">{</span>
  <span class="nv">ping_result</span><span class="o">=</span><span class="sb">`</span>phoenix_service_run ping<span class="sb">`</span>
  <span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ping_result</span><span class="k">}</span><span class="s2">"</span>
  <span class="k">case</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ping_result</span><span class="k">}</span><span class="s2">"</span> <span class="k">in</span>
    <span class="k">*</span>pong<span class="k">*</span><span class="p">)</span>
      <span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">name</span><span class="k">}</span><span class="s2"> is running."</span>
      <span class="p">;;</span>
  <span class="k">esac</span>
<span class="o">}</span>

phoenix_service_console<span class="o">()</span>
<span class="o">{</span>
  phoenix_service_run console
<span class="o">}</span>

phoenix_service_remote_console<span class="o">()</span>
<span class="o">{</span>
  phoenix_service_run remote_console
<span class="o">}</span>

load_rc_config <span class="nv">$name</span>
run_rc_command <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span></code></pre></figure>

<p>Make <strong>/usr/local/etc/rc.d/phoenix_service</strong> read only and executable.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">chmod </span>555 /usr/local/etc/rc.d/phoenix_service</code></pre></figure>

<p>Enable and configure the service in <strong>/etc/rc.conf</strong>.</p>

<p><strong>/etc/rc.conf</strong> lines to add</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">phoenix_service_enable</span><span class="o">=</span><span class="s2">"YES"</span>
<span class="nv">phoenix_service_port</span><span class="o">=</span>8248
<span class="nv">phoenix_service_node_name</span><span class="o">=</span><span class="s2">"suzaku"</span>
<span class="nv">phoenix_service_cookie</span><span class="o">=</span><span class="s2">"cookie-of-the-southern-flame"</span></code></pre></figure>

<p>The service can now be started.  If the service is enabled, it will automatically start when the machine boots.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service phoenix_service start
<span class="c"># POST new</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "Service Running", "body": "The Phoenix service is running on '</span><span class="s2">"</span><span class="si">$(</span><span class="nb">hostname</span><span class="si">)</span><span class="s2">"</span><span class="s1">'."}}'</span> http://localhost:8248/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://localhost:8248/api/memos/1</code></pre></figure>

<h3 id="optional-adding-a-release-to-the-systemwide-path">Optional: Adding a Release to the Systemwide Path</h3>

<p>Adding a release to the systemwide path is not necessary, but it can be convenient.
Create a directory for the convenience pass through script.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$INSTALL_DIR</span>/bin</code></pre></figure>

<p><strong>PORT</strong>, <strong>NODE_NAME</strong> and <strong>COOKIE</strong> need default values because <strong>vm.args</strong> has no useful default fallbacks.
There is no good way to automatically select a port, so 8080 is a hard coded default.
Add the following script to <strong>/usr/local/opt/bin/phoenix_service</strong>.</p>

<p><strong>/usr/local/opt/bin/phoenix_service</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#!/bin/sh</span>

<span class="nv">SCRIPT</span><span class="o">=</span><span class="si">$(</span><span class="nb">realpath</span> <span class="nv">$0</span><span class="si">)</span>
<span class="nv">BASENAME</span><span class="o">=</span><span class="si">$(</span><span class="nb">basename</span> <span class="nv">$SCRIPT</span><span class="si">)</span>
<span class="nv">BASEDIR</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="nv">$SCRIPT</span><span class="si">)</span>
<span class="nv">COMMAND</span><span class="o">=</span><span class="si">$(</span><span class="nb">realpath</span> <span class="nv">$BASEDIR</span>/../<span class="nv">$BASENAME</span>/bin/<span class="nv">$BASENAME</span><span class="si">)</span>

: <span class="k">${</span><span class="nv">NODE_NAME</span>:<span class="p">=</span><span class="k">${</span><span class="nv">BASENAME</span><span class="k">}}</span>
: <span class="k">${</span><span class="nv">COOKIE</span>:<span class="p">=</span><span class="k">${</span><span class="nv">BASENAME</span><span class="k">}}</span>
: <span class="k">${</span><span class="nv">PORT</span>:<span class="p">=</span><span class="s2">"8080"</span><span class="k">}</span>

<span class="nv">NODE_NAME</span><span class="o">=</span><span class="k">${</span><span class="nv">NODE_NAME</span><span class="k">}</span> <span class="se">\</span>
<span class="nv">COOKIE</span><span class="o">=</span><span class="k">${</span><span class="nv">COOKIE</span><span class="k">}</span> <span class="se">\</span>
<span class="nv">PORT</span><span class="o">=</span><span class="k">${</span><span class="nv">PORT</span><span class="k">}</span> <span class="se">\</span>
<span class="nv">RELX_REPLACE_OS_VARS</span><span class="o">=</span><span class="nb">true</span> <span class="se">\</span>
<span class="nv">$COMMAND</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span></code></pre></figure>

<p>Make the script executable.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">chmod</span> +x <span class="nv">$INSTALL_DIR</span>/bin/<span class="nv">$PROJECT</span></code></pre></figure>

<p>Add <strong>/usr/local/opt/bin</strong> to the global path in <strong>/etc/profile</strong> for <strong>sh</strong>,
and <strong>/etc/csh.cshrc</strong> for <strong>csh</strong>.
Consider updating the root path in <strong>/root/.cshrc</strong>.</p>

<p><strong>/usr/local/opt/bin</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PATH</span><span class="o">=</span>/usr/local/opt/bin:<span class="nv">$PATH</span>
<span class="nb">export </span>PATH</code></pre></figure>

<p><strong>/etc/csh.cshrc</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-csh" data-lang="csh">set path=(/usr/local/opt/bin $path)</code></pre></figure>

<p><strong>/root/.cshrc</strong> partial listing</p>

<figure class="highlight"><pre><code class="language-csh" data-lang="csh">set path = (/usr/local/opt/bin /sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin $HOME/bin)</code></pre></figure>

<p>Update the path in the current shell if necessary.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># sh bash</span>
<span class="nb">source</span> /etc/csh.cshrc
<span class="c"># csh tcsh</span>
<span class="nb">.</span> /etc/profile</code></pre></figure>

<p>Fix permissions if you want to be able to run as any user.
This may have security implications.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">chmod </span>755 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/bin/<span class="nv">$PROJECT</span>
<span class="nb">chmod </span>755 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/bin/nodetool
<span class="nb">chmod </span>755 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/releases/<span class="nv">$VERSION</span>/<span class="nv">$PROJECT</span>.sh
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/log
<span class="nb">chmod </span>777 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/log
<span class="nb">chmod </span>777 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/log/<span class="k">*</span>.<span class="k">*</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/tmp/erl_pipes/<span class="nv">$PROJECT</span>
<span class="nb">chmod </span>777 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/tmp/erl_pipes/<span class="nv">$PROJECT</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/running-config/
<span class="nb">chmod </span>777 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/running-config/
<span class="nb">chmod </span>666 <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>/running-config/<span class="k">*</span>.<span class="k">*</span>
<span class="nb">chown</span> <span class="nt">-R</span> <span class="nv">$PROJECT</span>:<span class="nv">$PROJECT</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span></code></pre></figure>

<p>The release can now be conveniently controlled.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># start service</span>
<span class="nv">NODE_NAME</span><span class="o">=</span>rudra <span class="nv">COOKIE</span><span class="o">=</span>treasure <span class="nv">PORT</span><span class="o">=</span>5678 phoenix_service start
<span class="c"># POST new</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "Service Running", "body": "The Phoenix service is running on '</span><span class="s2">"</span><span class="si">$(</span><span class="nb">hostname</span><span class="si">)</span><span class="s2">"</span><span class="s1">'."}}'</span> http://localhost:5678/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://localhost:5678/api/memos/1
<span class="c"># stop service</span>
<span class="nv">NODE_NAME</span><span class="o">=</span>rudra <span class="nv">COOKIE</span><span class="o">=</span>treasure phoenix_service stop</code></pre></figure>

<p>Setup complete.  Switch from root to a normal user.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">exit</span> <span class="c"># sh</span>
<span class="nb">exit</span> <span class="c"># su</span></code></pre></figure>

<h3 id="updating">Updating</h3>

<p>Casual updates on a development machine can be performed as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># brunch build --production # if using brunch</span>
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix phoenix.digest <span class="c"># if static assets could have changed</span>
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix release
su
sh
<span class="nv">PROJECT</span><span class="o">=</span>phoenix_service
<span class="nv">INSTALL_DIR</span><span class="o">=</span>/usr/local/opt
<span class="nv">VERSION</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat </span>rel/<span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span>/releases/start_erl.data | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f2</span><span class="si">)</span>
<span class="nv">INSTALL_TAR</span><span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/rel/<span class="nv">$PROJECT</span>/releases/<span class="nv">$VERSION</span>/<span class="nv">$PROJECT</span>.tar.gz
<span class="o">(</span><span class="nb">cd</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span><span class="p">;</span> <span class="se">\</span>
<span class="nb">tar</span> <span class="nt">-xf</span> <span class="nv">$INSTALL_TAR</span><span class="o">)</span>
<span class="nb">chown</span> <span class="nt">-R</span> <span class="nv">$PROJECT</span>:<span class="nv">$PROJECT</span> <span class="nv">$INSTALL_DIR</span>/<span class="nv">$PROJECT</span>
service <span class="nv">$PROJECT</span> restart
<span class="nb">exit</span> <span class="c"># sh</span>
<span class="nb">exit</span> <span class="c"># su</span></code></pre></figure>

<p>Note that the path permissions will need to be fixed again if
you added the release to the systemwide path and want to be able
to run it as any user.</p>

<h3 id="troubleshooting">Troubleshooting</h3>

<p>Make sure to set server to true in <strong>config/prod.exs</strong>.
If the server is mysteriously not working, start the release
with console to see error messages.</p>

<h3 id="what-next">What Next?</h3>

<p>Consider looking into <a href="https://github.com/boldpoker/edeliver">edeliver</a> for deployment.
“edeliver is based on deliver and provides a bash script to build and deploy
Elixir and Erlang applications and perform hot-code upgrades.”</p>

<h2 id="references">References:</h2>
<ul>
  <li><a href="http://www.phoenixframework.org/docs/deployment">Phoenix, Deployment</a></li>
  <li><a href="http://learnwithjeff.com/blog/2015/10/03/building-a-json-api-with-phoenix/">Phoenix, Building a JSON API With Phoenix</a></li>
  <li><a href="https://renatomoya.github.io/2015/05/09/Building-a-versioned-REST-API-with-Phoenix-Framework.html">Phoenix, Building a versioned REST API with Phoenix Framework</a></li>
  <li><a href="https://github.com/phoenixframework/phoenix/issues/962">Phoenix, Default (4000) port busy</a></li>
  <li><a href="http://stackoverflow.com/questions/30540466/start-phoenix-app-with-cowboy-server-on-different-port">Phoenix, Start Phoenix app with cowboy server on different port</a></li>
  <li><a href="https://github.com/phoenixframework/phoenix/issues/354">Phoenix, does not evaluate Env config overrides at runtime</a></li>
  <li><a href="https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd.html">Phoenix, Installing Phoenix, Elixir and PostgreSQL on FreeBSD</a></li>
  <li><a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">Phoenix, A Shell Script for Working with Phoenix JSON APIs</a></li>
  <li><a href="https://github.com/bitwalker/exrm">Elixir Release Manager</a></li>
  <li><a href="https://exrm.readme.io/docs/release-configuration">Elixir, exrm, Release Configuration</a></li>
  <li><a href="https://github.com/bitwalker/exrm/issues/42">Elixir, exrm, Where should I place app.conf and vm.args</a></li>
  <li><a href="https://hex.pm/packages?search=exrm">Elixir, exrm, Packages on Hex</a></li>
  <li><a href="https://github.com/boldpoker/edeliver">Elixir, edeliver</a></li>
  <li><a href="http://elixir-lang.org/docs/stable/mix/Mix.Config.html">Elixir, Mix.Config</a></li>
  <li><a href="https://sgeos.github.io/elixir/erlang/2016/01/16/elixir-as-a-service_on_freebsd.html">Elixir as a Service on FreeBSD</a></li>
  <li><a href="https://github.com/adamkittelson/apathy-drive-ex/blob/master/config/deploy.rb">Ruby, adamkittelson’s Cap File</a></li>
  <li><a href="https://jasonk2600.wordpress.com/2010/01/11/installing-postgresql-on-freebsd/">PostgreSQL, Installing PostgreSQL on FreeBSD</a></li>
  <li><a href="http://www.grymoire.com/Unix/Sh.html">Sh - the Bourne Shell</a></li>
  <li><a href="https://forums.freebsd.org/threads/9120/">FreeBSD, KDE4 localization</a></li>
  <li><a href="https://sgeos.github.io/freebsd/ion/dtn/2016/02/15/ion-dtn-as-a-service-on-freebsd.html">ION DTN as a Service on FreeBSD</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="phoenix" /><category term="elixir" /><category term="erlang" /><category term="freebsd" /></entry><entry><title type="html">Installing Phoenix, Elixir and PostgreSQL on FreeBSD</title><link href="https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd.html" rel="alternate" type="text/html" title="Installing Phoenix, Elixir and PostgreSQL on FreeBSD" /><published>2016-03-19T07:35:02+00:00</published><updated>2016-03-19T07:35:02+00:00</updated><id>https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd</id><content type="html" xml:base="https://sgeos.github.io/phoenix/elixir/postgresql/freebsd/2016/03/19/installing-phoenix-elixir-and-postgresql-on-freebsd.html"><![CDATA[<!-- A28 -->
<script>console.log("A28");</script>

<p>This post covers installing Phoenix, Elixir and PostgreSQL on a clean installation of FreeBSD.
The post assumes FreeBSD is installed and you have created an unprivileged user.
FreeBSD installation instructions can be found <a href="https://www.freebsd.org/doc/handbook/bsdinstall.html">here</a>.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2016-03-19 07:35:02 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
FreeBSD 11.0-CURRENT <span class="c">#0 r296925: Wed Mar 16 20:53:04 JST 2016     root@mirage.sennue.com:/usr/obj/usr/src/sys/MIRAGE_KERNEL  amd64</span>
<span class="nv">$ </span>psql <span class="nt">--version</span>
psql <span class="o">(</span>PostgreSQL<span class="o">)</span> 9.4.6
<span class="nv">$ </span>mix hex.info
Hex:    0.11.3
Elixir: 1.2.3
OTP:    18.2.4
<span class="k">*</span> snip <span class="k">*</span>
<span class="nv">$ </span>mix phoenix.new <span class="nt">-v</span>
Phoenix v1.1.4</code></pre></figure>

<h2 id="bootstrapping-the-system">Bootstrapping the system</h2>

<p>Login as root and install <strong>portmaster</strong>.
If you are using packages instead of ports, you will need to
install packages instead of using <strong>portmaster</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">portsnap fetch extract
<span class="o">(</span><span class="nb">cd</span> /usr/ports/ports-mgmt/portmaster <span class="o">&amp;&amp;</span> make config-recursive <span class="o">&amp;&amp;</span> make config-recursive <span class="o">&amp;&amp;</span> make <span class="nb">install </span>clean<span class="o">)</span>
portmaster security/sudo</code></pre></figure>

<p>The following utilities may also be useful.
Note that <strong>ftp/curl</strong> is used at the end of the post.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">portmaster ports-mgmt/portupgrade devel/git shells/bash editors/vim ftp/curl ftp/wget</code></pre></figure>

<h2 id="installing-postgresql">Installing PostgreSQL</h2>

<p>Install <strong>databases/postgresql94-server</strong> as root.
If you would rather install a different version, do that instead.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">portmaster databases/postgresql94-server</code></pre></figure>

<p>Initialize the database.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service postgresql initdb</code></pre></figure>

<p>If you want to enable remote connections,
add the following line to <strong>/usr/local/pgsql/data/postgresql.conf</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">listen_addresses <span class="o">=</span> <span class="s1">'*'</span></code></pre></figure>

<p>If you want to change the port PostgreSQL listens on,
add the following line to <strong>/usr/local/pgsql/data/postgresql.conf</strong>.
Replace 5432 with the port you want to use.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">port <span class="o">=</span> 5432                            <span class="c"># (change requires restart)</span></code></pre></figure>

<p>To use password hash authentication,
add the following line to <strong>/usr/local/pgsql/data/pg_hba.conf</strong>.
Replace 192.168.0.0/16 with your own network.
Consider changing “trust” to “md5” for local and loopback IP address connections.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">host    all             all             192.168.0.0/16          md5</code></pre></figure>

<p>Enable PostgreSQL in <strong>/etc/rc.conf</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">echo</span> <span class="s1">'postgresql_enable="YES"'</span> <span class="o">&gt;&gt;</span> /etc/rc.conf</code></pre></figure>

<p>Start PostgreSQL.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">service postgresql start</code></pre></figure>

<p>Add a PostgreSQL super-user with database and role creation privileges.
Replace username with your unprivileged FreeBSD user login.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">su pgsql
createuser <span class="nt">-sdrP</span> username</code></pre></figure>

<p>You should now be able to start <strong>psql</strong> as your non-root user.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">psql</code></pre></figure>

<h2 id="installing-elixir-and-phoenix">Installing Elixir and Phoenix</h2>

<p>Install Elixir and Phoenix.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">sudo </span>portmaster lang/elixir
mix local.hex
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez</code></pre></figure>

<p>Optionally, install <strong>npm</strong> and <strong>brunch</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">sudo </span>portmaster www/node www/npm
<span class="nb">sudo </span>npm <span class="nb">install</span> <span class="nt">-g</span> brunch</code></pre></figure>

<h1 id="creating-a-sample-project">Creating a Sample Project</h1>

<p>Create a new Phoenix project.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.new phoenix_service <span class="nt">--no-brunch</span>
<span class="nb">cd </span>phoenix_service
mix ecto.create</code></pre></figure>

<p>Create a simple JSON memo service.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.gen.json Memo memos title:string body:string</code></pre></figure>

<p>Revise the <strong>web/router.ex</strong> file.
The “/api” scope needs to be uncommented and the “/memos” route needs to be added.</p>

<p><strong>web/router.ex file</strong></p>

<figure class="highlight"><pre><code class="language-elixer" data-lang="elixer">defmodule PhoenixService.Router do
  use PhoenixService.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", PhoenixService do
    pipe_through :browser # Use the default browser stack
  
    get "/", PageController, :index
  end

  scope "/api", PhoenixService do
    pipe_through :api

    resources "/memos", MemoController, except: [:new, :edit]
  end
end</code></pre></figure>

<p>Run the migration.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix ecto.migrate</code></pre></figure>

<p>Tests should pass.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix <span class="nb">test</span></code></pre></figure>

<h1 id="running-the-sample-project">Running the Sample Project</h1>

<p>Start the server.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">mix phoenix.server</code></pre></figure>

<p>The following <strong>curl</strong> commands can be used to interact with the running JSON API.
My <a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">previous post</a> covers a shell script for interacting with this sample app.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># POST new</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "New Title", "body": "This is the new memo body."}}'</span> http://localhost:4000/api/memos
<span class="c"># PATCH id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> PATCH <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "Patched Title"}}'</span> http://localhost:4000/api/memos/1
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> PATCH <span class="nt">-d</span> <span class="s1">'{"memo": {"body": "Patched memo body."}}'</span> http://localhost:4000/api/memos/1
<span class="c"># PUT id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> PUT <span class="nt">-d</span> <span class="s1">'{"memo": {"title": "Updated Title", "body": "Updated memo body."}}'</span> http://localhost:4000/api/memos/1
<span class="c"># GET all</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://localhost:4000/api/memos
<span class="c"># GET id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> http://localhost:4000/api/memos/1
<span class="c"># DELETE id 1</span>
curl <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-X</span> DELETE http://localhost:4000/api/memos/1</code></pre></figure>

<h2 id="references">References:</h2>
<ul>
  <li><a href="http://www.phoenixframework.org/docs/deployment">Phoenix, Deployment</a></li>
  <li><a href="http://learnwithjeff.com/blog/2015/10/03/building-a-json-api-with-phoenix/">Phoenix, Building a JSON API With Phoenix</a></li>
  <li><a href="https://renatomoya.github.io/2015/05/09/Building-a-versioned-REST-API-with-Phoenix-Framework.html">Phoenix, Building a versioned REST API with Phoenix Framework</a></li>
  <li><a href="https://sgeos.github.io/phoenix/elixir/sh/2016/03/19/a-shell-script-for-working-with-phoenix-json-apis.html">Phoenix, A Shell Script for Working with Phoenix JSON APIs</a></li>
  <li><a href="https://sgeos.github.io/elixir/erlang/2016/01/16/elixir-as-a-service_on_freebsd.html">Elixir as a Service on FreeBSD</a></li>
  <li><a href="https://jasonk2600.wordpress.com/2010/01/11/installing-postgresql-on-freebsd/">PostgreSQL, Installing PostgreSQL on FreeBSD</a></li>
  <li><a href="http://www.rhyous.com/2010/08/27/installing-and-using-postgresql-on-freebsd/">PostgreSQL, Installing and Using Postgresql on FreeBSD</a></li>
  <li><a href="http://www.freebsddiary.org/postgresql.php">PostgreSQL, installation and configuration</a></li>
  <li><a href="http://www.grymoire.com/Unix/Sh.html">Sh - the Bourne Shell</a></li>
  <li><a href="https://www.freebsd.org/doc/handbook/bsdinstall.html">FreeBSD, Installing FreeBSD</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="phoenix" /><category term="elixir" /><category term="postgresql" /><category term="freebsd" /></entry></feed>