uniquely matches one of the detached
+# sessions (from "screen -list").
+#
+# debugger_command =
+# PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen
+# -dmS $process_name gdb $daemon_directory/$process_name
+# $process_id & sleep 1
+
+# INSTALL-TIME CONFIGURATION INFORMATION
+#
+# The following parameters are used when installing a new Postfix version.
+#
+# sendmail_path: The full pathname of the Postfix sendmail command.
+# This is the Sendmail-compatible mail posting interface.
+#
+sendmail_path = /usr/sbin/sendmail.postfix
+
+# newaliases_path: The full pathname of the Postfix newaliases command.
+# This is the Sendmail-compatible command to build alias databases.
+#
+newaliases_path = /usr/bin/newaliases.postfix
+
+# mailq_path: The full pathname of the Postfix mailq command. This
+# is the Sendmail-compatible mail queue listing command.
+#
+mailq_path = /usr/bin/mailq.postfix
+
+# setgid_group: The group for mail submission and queue management
+# commands. This must be a group name with a numerical group ID that
+# is not shared with other accounts, not even with the Postfix account.
+#
+setgid_group = postdrop
+
+# html_directory: The location of the Postfix HTML documentation.
+#
+html_directory = no
+
+# manpage_directory: The location of the Postfix on-line manual pages.
+#
+manpage_directory = /usr/share/man
+
+# sample_directory: The location of the Postfix sample configuration files.
+# This parameter is obsolete as of Postfix 2.1.
+#
+sample_directory = /usr/share/doc/postfix-2.4.3/samples
+
+# readme_directory: The location of the Postfix README files.
+#
+readme_directory = /usr/share/doc/postfix-2.4.3/README_FILES
diff --git a/Chapter 4/modules/postfix/files/master.cf b/Chapter 4/modules/postfix/files/master.cf
new file mode 100644
index 0000000..df0c4f2
--- /dev/null
+++ b/Chapter 4/modules/postfix/files/master.cf
@@ -0,0 +1,99 @@
+#
+# Postfix master process configuration file. For details on the format
+# of the file, see the master(5) manual page (command: "man 5 master").
+#
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (yes) (never) (100)
+# ==========================================================================
+smtp inet n - n - - smtpd
+#submission inet n - n - - smtpd
+# -o smtpd_enforce_tls=yes
+# -o smtpd_sasl_auth_enable=yes
+# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+#smtps inet n - n - - smtpd
+# -o smtpd_tls_wrappermode=yes
+# -o smtpd_sasl_auth_enable=yes
+# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+#628 inet n - n - - qmqpd
+pickup fifo n - n 60 1 pickup
+cleanup unix n - n - 0 cleanup
+qmgr fifo n - n 300 1 qmgr
+#qmgr fifo n - n 300 1 oqmgr
+tlsmgr unix - - n 1000? 1 tlsmgr
+rewrite unix - - n - - trivial-rewrite
+bounce unix - - n - 0 bounce
+defer unix - - n - 0 bounce
+trace unix - - n - 0 bounce
+verify unix - - n - 1 verify
+flush unix n - n 1000? 0 flush
+proxymap unix - - n - - proxymap
+smtp unix - - n - - smtp
+# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
+relay unix - - n - - smtp
+ -o fallback_relay=
+# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
+showq unix n - n - - showq
+error unix - - n - - error
+retry unix - - n - - error
+discard unix - - n - - discard
+local unix - n n - - local
+virtual unix - n n - - virtual
+lmtp unix - - n - - lmtp
+anvil unix - - n - 1 anvil
+scache unix - - n - 1 scache
+#
+# ====================================================================
+# Interfaces to non-Postfix software. Be sure to examine the manual
+# pages of the non-Postfix software to find out what options it wants.
+#
+# Many of the following services use the Postfix pipe(8) delivery
+# agent. See the pipe(8) man page for information about ${recipient}
+# and other message envelope options.
+# ====================================================================
+#
+# maildrop. See the Postfix MAILDROP_README file for details.
+# Also specify in main.cf: maildrop_destination_recipient_limit=1
+#
+#maildrop unix - n n - - pipe
+# flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
+#
+# ====================================================================
+#
+# The Cyrus deliver program has changed incompatibly, multiple times.
+#
+#old-cyrus unix - n n - - pipe
+# flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m ${extension} ${user}
+#
+# ====================================================================
+#
+# Cyrus 2.1.5 (Amos Gouaux)
+# Also specify in main.cf: cyrus_destination_recipient_limit=1
+#
+#cyrus unix - n n - - pipe
+# user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r ${sender} -m ${extension} ${user}
+#
+# ====================================================================
+#
+# See the Postfix UUCP_README file for configuration details.
+#
+#uucp unix - n n - - pipe
+# flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
+#
+# ====================================================================
+#
+# Other external delivery methods.
+#
+#ifmail unix - n n - - pipe
+# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
+#
+#bsmtp unix - n n - - pipe
+# flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient
+#
+#scalemail-backend unix - n n - 2 pipe
+# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store
+# ${nexthop} ${user} ${extension}
+#
+#mailman unix - n n - - pipe
+# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
+# ${nexthop} ${user}
diff --git a/Chapter 4/modules/postfix/manifests/.DS_Store b/Chapter 4/modules/postfix/manifests/.DS_Store
new file mode 100644
index 0000000..ac94a93
Binary files /dev/null and b/Chapter 4/modules/postfix/manifests/.DS_Store differ
diff --git a/Chapter 4/modules/postfix/manifests/init.pp b/Chapter 4/modules/postfix/manifests/init.pp
new file mode 100644
index 0000000..d19d0d5
--- /dev/null
+++ b/Chapter 4/modules/postfix/manifests/init.pp
@@ -0,0 +1,36 @@
+class postfix {
+
+$mailadmin = "postmaster@$domain"
+
+$packagelist = ["postfix.$architecture", "postfix-pflogsumm.$architecture"]
+
+package { $packagelist:
+ ensure => "installed"
+}
+
+postfix::postfix_files {
+
+"/etc/aliases.db":
+ mode => "0640",
+ source => "aliases.db";
+
+"/etc/postfix/main.cf":
+ source => "main.cf";
+
+"/etc/postfix/master.cf":
+ source => "master.cf"
+}
+service { "postfix":
+ enable => "true",
+ ensure => "running",
+ require => Package["postfix.$architecture"]
+}
+
+cron { pflogsumm:
+ hour => 2,
+ minute => 15,
+ user => mail,
+ command => "/usr/sbin/pflogsumm -d yesterday /var/log/maillog | mail -s 'pflogsumm from $fqdn' $mailadmin",
+ require => Package["postfix-pflogsumm.$architecture"]
+}
+}
diff --git a/Chapter 4/modules/postfix/manifests/postfix_files.pp b/Chapter 4/modules/postfix/manifests/postfix_files.pp
new file mode 100644
index 0000000..bf2ed6d
--- /dev/null
+++ b/Chapter 4/modules/postfix/manifests/postfix_files.pp
@@ -0,0 +1,13 @@
+define postfix::postfix_files($owner = root, $group = root, $mode = 644, $source, $backup = false, $recurse = false, $ensure = file) {
+
+ file { $name:
+ mode => $mode,
+ owner => $owner,
+ group => $group,
+ backup => $backup,
+ recurse => $recurse,
+ ensure => $ensure,
+ require => Package["postfix.$architecture"],
+ source => "puppet:///postfix/$source"
+ }
+}
diff --git a/Chapter 4/my.cnf b/Chapter 4/my.cnf
new file mode 100644
index 0000000..2e035c8
--- /dev/null
+++ b/Chapter 4/my.cnf
@@ -0,0 +1,11 @@
+[mysqld]
+datadir=/var/lib/mysql
+socket=/var/lib/mysql/mysql.sock
+user=mysql
+# Default to using old password format for compatibility with mysql 3.x
+# clients (those using the mysqlclient10 compatibility package).
+old_passwords=1
+
+[mysqld_safe]
+log-error=/var/log/mysqld.log
+pid-file=/var/run/mysqld/mysqld.pid
diff --git a/Chapter 4/puppet.conf b/Chapter 4/puppet.conf
new file mode 100644
index 0000000..400ee35
--- /dev/null
+++ b/Chapter 4/puppet.conf
@@ -0,0 +1,2 @@
+[puppetmasterd]
+modulepath = /etc/puppet/modules
diff --git a/Chapter 6/.DS_Store b/Chapter 6/.DS_Store
new file mode 100644
index 0000000..369284a
Binary files /dev/null and b/Chapter 6/.DS_Store differ
diff --git a/Chapter 6/apache_puppet.conf b/Chapter 6/apache_puppet.conf
new file mode 100644
index 0000000..e0d5fd9
--- /dev/null
+++ b/Chapter 6/apache_puppet.conf
@@ -0,0 +1,62 @@
+Listen 8140
+PidFile /var/www/puppet/run/balancer.pid
+User puppet
+Group puppet
+
+LoadModule proxy_module modules/mod_proxy.so
+LoadModule proxy_http_module modules/mod_proxy_http.so
+LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
+LoadModule headers_module modules/mod_headers.so
+LoadModule ssl_module modules/mod_ssl.so
+LoadModule authz_host_module modules/mod_authz_host.so
+LoadModule log_config_module modules/mod_log_config.so
+
+
+ Options FollowSymLinks
+ AllowOverride None
+ Order deny,allow
+ Deny from all
+
+
+
+ BalancerMember http://127.0.0.1:18140 keepalive=on max=2 retry=30
+
+ BalancerMember http://127.0.0.1:18141 keepalive=on max=2 retry=30
+
+
+
+
+ SSLEngine on
+ SSLCipherSuite SSLv2:-LOW:-EXPORT:RC4+RSA
+ SSLCertificateFile /var/www/puppet/ssl/certs/puppetmaster.testing.com.pem
+ SSLCertificateKeyFile /var/www/puppet/ssl/private_keys/puppetmaster.testing.com.pem
+ SSLCertificateChainFile /var/www/puppet/ssl/ca/ca_crt.pem
+ SSLCACertificateFile /var/www/puppet/ssl/ca/ca_crt.pem
+ SSLCARevocationFile /var/www/puppet/ssl/ca/ca_crl.pem
+ SSLVerifyClient optional
+ SSLVerifyDepth 1
+ SSLOptions +StdEnvVars
+
+ RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
+ # Store the client DN in a header
+ RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
+ # And store whether the cert verification was a success
+ RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
+
+
+ SetHandler balancer-manager
+ Order allow,deny
+ Allow from all
+
+
+ ProxyPass / balancer://puppetmaster.testing.com:8140/ timeout=180
+ ProxyPassReverse / balancer://puppetmaster.testing.com:8140/
+ ProxyPreserveHost on
+ SetEnv force-proxy-request-1.0 1
+ SetEnv proxy-nokeepalive 1
+
+ ErrorLog /var/www/puppet/balancer_error.log
+ CustomLog /var/www/puppet/balancer_access.log combined
+ CustomLog /var/www/puppet/balancer_ssl_request.log \
+ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
+
diff --git a/Chapter 6/ext_node_mysql.pl b/Chapter 6/ext_node_mysql.pl
new file mode 100644
index 0000000..0f75423
--- /dev/null
+++ b/Chapter 6/ext_node_mysql.pl
@@ -0,0 +1,50 @@
+#!/usr/bin/perl -w
+use strict;
+use YAML qw( Dump );
+use DBI;
+
+my $hostname = shift || die "No hostname passed";
+
+$hostname =~ /^(\w+)\.(\w+)\.(\w{3})$/
+ or die "Invalid hostname: $hostname";
+
+my ( $host, $domain, $net ) = ( $1, $2, $3 );
+
+# MySQL Configuration
+my $data_source = "dbi:mysql:database=puppet;host=localhost";
+my $username = "puppet";
+my $password = "password";
+
+# Connect to the server
+my $dbh = DBI->connect($data_source, $username, $password)
+ or die $DBI::errstr;
+
+# Build the query
+my $sth = $dbh->prepare( qq{SELECT class FROM nodes WHERE node = '$hostname'})
+ or die "Can't prepare statement: $DBI::errstr";
+
+# Execute the query
+my $rc = $sth->execute
+ or die "Can't execute statement: $DBI::errstr";
+
+# Set parameters
+my %parameters = (
+ puppet_server => "puppet.$domain.$net"
+ );
+
+# Set classes
+my @class;
+while (my @row=$sth->fetchrow_array)
+ { push(@class,@row) }
+
+# Check for problems
+die $sth->errstr if $sth->err;
+
+# Disconnect from database
+$dbh->disconnect;
+
+# Print the YAML
+print Dump( {
+ classes => \@class,
+ parameters => \%parameters,
+} );
diff --git a/Chapter 6/external_nodes.pl b/Chapter 6/external_nodes.pl
new file mode 100644
index 0000000..f7a000d
--- /dev/null
+++ b/Chapter 6/external_nodes.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+use strict;
+use YAML qw( Dump );
+
+my $hostname = shift || die "No hostname passed";
+
+$hostname =~ /^(\w+)\.(\w+)\.(\w{3})$/
+ or die "Invalid hostname: $hostname";
+
+my ( $host, $domain, $net ) = ( $1, $2, $3 );
+
+my @classes = ( 'baseapps', $domain, );
+my %parameters = (
+ puppet_server => "puppet.$domain.$net"
+ );
+
+print Dump( {
+ classes => \@classes,
+ parameters => \%parameters,
+} );
diff --git a/Chapter 6/ldap_nodes.ldif b/Chapter 6/ldap_nodes.ldif
new file mode 100644
index 0000000..52cba3b
--- /dev/null
+++ b/Chapter 6/ldap_nodes.ldif
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+# LDIF Export for: ou=Hosts,dc=testing,dc=com
+dn: ou=Hosts,dc=testing,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: Hosts
+
+dn: cn=basenode,ou=Hosts,dc=testing,dc=com
+cn: basenode
+description: Basenode
+objectClass: device
+objectClass: top
+objectClass: puppetClient
+puppetclass: baseapps
+
+dn: cn=webserver,ou=Hosts,dc=testing,dc=com
+cn: webserver
+description: Basenode
+objectClass: device
+objectClass: top
+objectClass: puppetClient
+parentnode: basenode
+puppetclass: apache
+puppetclass: squid
+puppetclass: named
+
+
diff --git a/Chapter 6/mongrel_puppetmasterd.sh b/Chapter 6/mongrel_puppetmasterd.sh
new file mode 100644
index 0000000..6761ac6
--- /dev/null
+++ b/Chapter 6/mongrel_puppetmasterd.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# name: mongrel_puppetmasterd
+# Start a Puppet Master Server instance.
+
+if ! [[ "$1" -gt 0 ]]; then
+echo "ERROR: You must provide a port to run this puppet master on."
+echo "Ensure your apache load balancer is configured to talk to these servers"
+exit 1
+fi
+
+MASTERPORT="$1"
+shift
+
+puppetmasterd \
+--pidfile=/var/www/puppet/run/puppetmasterd."${MASTERPORT}".pid \
+--servertype=mongrel \
+--masterport="${MASTERPORT}" \
+$*
+
+
diff --git a/Chapter 6/puppet.conf b/Chapter 6/puppet.conf
new file mode 100644
index 0000000..e76987d
--- /dev/null
+++ b/Chapter 6/puppet.conf
@@ -0,0 +1,56 @@
+Listen 8140
+PidFile /var/www/puppet/run/balancer.pid
+User puppet
+Group puppet
+
+#LoadModule proxy_module modules/mod_proxy.so
+#LoadModule proxy_http_module modules/mod_proxy_http.so
+#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
+#LoadModule headers_module modules/mod_headers.so
+#LoadModule ssl_module modules/mod_ssl.so
+#LoadModule authz_host_module modules/mod_authz_host.so
+#LoadModule log_config_module modules/mod_log_config.so
+
+
+ Options FollowSymLinks
+ AllowOverride None
+ Order deny,allow
+ Deny from all
+
+
+
+ BalancerMember http://127.0.0.1:18140
+ BalancerMember http://127.0.0.1:18141
+
+
+
+ SSLEngine on
+ SSLCipherSuite SSLv2:-LOW:-EXPORT:RC4+RSA
+ SSLCertificateFile /var/www/puppet/ssl/certs/puppetmaster.testing.com.pem
+ SSLCertificateKeyFile /var/www/puppet/ssl/private_keys/puppetmaster.testing.com.pem
+ SSLCertificateChainFile /var/www/puppet/ssl/ca/ca_crt.pem
+ SSLCACertificateFile /var/www/puppet/ssl/ca/ca_crt.pem
+ SSLCARevocationFile /var/www/puppet/ssl/ca/ca_crl.pem
+ SSLVerifyClient optional
+ SSLVerifyDepth 1
+ SSLOptions +StdEnvVars
+
+ # Store the client DN in a header
+ RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
+ # And store whether the cert verification was a success
+ RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
+
+ SetHandler balancer-manager
+ Order allow,deny
+ Allow from all
+
+
+ ProxyPass / balancer://puppetmaster.testing.com:8140/
+ ProxyPassReverse / balancer://puppetmaster.testing.com:8140/
+ ProxyPreserveHost on
+
+ ErrorLog /var/www/puppet/balancer_error.log
+ CustomLog /var/www/puppet/balancer_access.log combined
+ CustomLog /var/www/puppet/balancer_ssl_request.log \
+ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
+
diff --git a/Chapter 7/.DS_Store b/Chapter 7/.DS_Store
new file mode 100644
index 0000000..32d4911
Binary files /dev/null and b/Chapter 7/.DS_Store differ
diff --git a/Chapter 7/provider/parsed.rb b/Chapter 7/provider/parsed.rb
new file mode 100644
index 0000000..989cb3b
--- /dev/null
+++ b/Chapter 7/provider/parsed.rb
@@ -0,0 +1,18 @@
+require 'puppet/provider/parsedfile'
+
+shells = "/etc/shells"
+
+Puppet::Type.type(:shells).provide(:parsed,
+ :parent => Puppet::Provider::ParsedFile,
+ :default_target => shells,
+ :filetype => :flat
+ ) do
+
+ desc "The shells provider that uses the ParsedFile class"
+
+ confine :exists => shells
+ text_line :comment, :match => /^#/;
+ text_line :blank, :match => /^\s*$/;
+
+ record_line :parsed, :fields => %w{name}
+end
diff --git a/Chapter 7/type/.DS_Store b/Chapter 7/type/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Chapter 7/type/.DS_Store differ
diff --git a/Chapter 7/type/shells.rb b/Chapter 7/type/shells.rb
new file mode 100644
index 0000000..7616318
--- /dev/null
+++ b/Chapter 7/type/shells.rb
@@ -0,0 +1,29 @@
+module Puppet
+ newtype(:shells) do
+
+ @doc = "Manages shells in /etc/shells. For example::
+
+ shells { \"/bin/bash\":
+ ensure => present,
+ }
+ There is also an optional target attribute if your
+ shells file is located elsewhere."
+
+ ensurable
+
+ newparam(:shell, :namevar => true) do
+ desc "The shell to manage"
+ isnamevar
+ end
+
+ newproperty(:target) do
+ desc "Location of shells file"
+ defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile)
+ @resource.class.defaultprovider.default_target
+ else
+ nil
+ end
+ }
+ end
+ end
+end
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..e3353a4
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,27 @@
+Freeware License, some rights reserved
+
+Copyright (c) 2007 James Turnbull
+
+Permission is hereby granted, free of charge, to anyone obtaining a copy
+of this software and associated documentation files (the "Software"),
+to work with the Software within the limits of freeware distribution and fair use.
+This includes the rights to use, copy, and modify the Software for personal use.
+Users are also allowed and encouraged to submit corrections and modifications
+to the Software for the benefit of other users.
+
+It is not allowed to reuse, modify, or redistribute the Software for
+commercial use in any way, or for a user’s educational materials such as books
+or blog articles without prior permission from the copyright holder.
+
+The above copyright notice and this permission notice need to be included
+in all copies or substantial portions of the software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ee1f971
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+#Apress Source Code
+
+This repository accompanies [*Pulling Strings with Puppet*](http://www.apress.com/9781590599785) by James Turnbull (Apress, 2007).
+
+![Cover image](9781590599785.jpg)
+
+Download the files as a zip using the green button, or clone the repository to your machine using Git.
+
+##Releases
+
+Release v1.0 corresponds to the code in the published book, without corrections or updates.
+
+##Contributions
+
+See the file Contributing.md for more information on how you can contribute to this repository.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..2489e64
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,21 @@
+Pulling Strings with Puppet: System Administration Made Easy
+============================================================
+
+This source code package contains source code from three chapters:
+
+Chapter 4
+---------
+
+This directory contains all of the example manifests and modules demonstrated in Chapter 4.
+
+Chapter 6
+---------
+
+This directory contains the example External Node scripts, LDAP directory configuraton, and
+Mongrel with Apache as a load balancer configurations.
+
+Chapter 7
+---------
+
+This directory contains the example type, shells, and the associated provider we demonstrated
+in Chapter 7.
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 0000000..f6005ad
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,14 @@
+# Contributing to Apress Source Code
+
+Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers.
+
+## How to Contribute
+
+1. Make sure you have a GitHub account.
+2. Fork the repository for the relevant book.
+3. Create a new branch on which to make your change, e.g.
+`git checkout -b my_code_contribution`
+4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted.
+5. Submit a pull request.
+
+Thank you for your contribution!
\ No newline at end of file