bash replace multiline text in file, with pattern - bash

Hi I need to edit some file but I dinĀ“t want to do it manually, I know that using sed command I can edit files using command line, but in this case, I don't know how to match the pattern to edit. for example I have this file:
(
AMI1
{
type patch; // <- relpace patch by cyclicAMI;
nFaces 1350;
startFace 2433406;
}
inlet
{
type patch;
nFaces 1125;
startFace 2434756;
}
outlet
{
type patch;
nFaces 1125;
startFace 2435881;
}
AMI2
{
type patch; // <- relpace patch by cyclicAMI;
nFaces 2850;
startFace 2440606;
}
)
And I want to edit ONLY the AMI keys to look like this:
(
AMI1
{
type cyclicAMI; // <-- Replaced
inGroups 1(cyclicAMI); // <-- Add
nFaces 1350;
startFace 2433406;
matchTolerance 0.0001; // <-- Add
transform noOrdering; // <-- Add
neighbourPatch AMI2; // <-- Add AMI2 in AMI1
}
inlet
{
type patch;
nFaces 1125;
startFace 2434756;
}
outlet
{
type patch;
nFaces 1125;
startFace 2435881;
}
AMI2
{
type cyclicAMI; // <-- Replaced
inGroups 1(cyclicAMI); // <-- Add
nFaces 2850;
startFace 2440606;
matchTolerance 0.0001; // <-- Add
transform noOrdering; // <-- Add
neighbourPatch AMI1; // <-- Add AMI1 in AMI2
}
)

Not overly elegant, but working.
state=0
while IFS= read -r line; do
case "${line// }" in
AMI[12]) state=${line##*AMI}
echo "$line";;
typepatch\;*) echo " type cyclicAMI;"
echo " inGroups 1(cyclicAMI);";;
else
echo "$line"
fi;;
\}) if [ "$state" != 0 ]; then
echo " matchTolerance 0.0001;"
echo " transform noOrdering;"
echo " neighbourPatch AMI$((3-state));"
echo " }"
state=0
else
echo " }"
fi;;
*) echo "$line"
esac
done < textfile.txt

Related

Remove mutiline string after equal sign

I am trying to get IAM policies from AWS and add it to a text file in the below specific format. I want to delete everything after policy= in the file before end of the "}" bracket.
This is the text file sample I have. But the original file can have multiple instances of example_policy in the same file.
"example1_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example2_policy" {
name="example2"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example3_policy" {
name="example3"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
Expected Output:
"example1_policy" {
name="example1"
policy=
}
"example2_policy" {
name="example2"
policy=
}
"example3_policy" {
name="example3"
policy=
}
or
"example1_policy" {
name="example1"
policy=<placeholder>
}
"example2_policy" {
name="example2"
policy=<placeholder>
}
"example3_policy" {
name="example3"
policy=<placeholder>
}
As per #Wiktor's comment I tried out this command
sed -i '/policy=/,/^\s*)\s*$/d' test.txt
Output: policy= should remain intact.
"example_policy" {
name="example"
}
You can use the following GNU sed command:
sed -i '/policy=/,/^\s*)\s*$/{/policy=/!d};s/\(policy=\).*/\1<placehlder>/' file
See the online demo. Details:
/policy=/,/^\s*)\s*$/ - finds blocks of lines between a line with policy= and a line that contains only a ) char enclosed with zero or more whitespaces
{/policy=/!d} - prevents the first line in the found block to be removed and removed the other line(s)
s/\(policy=\).*/\1<placehlder>/ - replaces all after policy= with <placeholder>.
If there is a need to match policy=, then any chars up to ( and then up to the next balanced ) char, you can use perl command like
perl -0777 -i -pe 's/^(\h*policy=)[^()]*(\((?:[^()]++|(?2))*\))/$1<placehlder>/gm' file
Details:
-0777 - read the file into a single string so that line breaks and the lines
-i - inline file change
^ - start of a line (due to m flag)
(\h*policy=) - Group 1 ($1): zero or more horizontal whitespaces
[^()]* - zero or more chars other than ( and )
(\((?:[^()]++|(?2))*\)) - Group 2 (for recursion): \( - a ( char, (?:[^()]++|(?2))* - zero or more sequences of one or more chars other than ( and ) or the whole Group 2 pattern recursed, and \) matches a ) char
$1<placehlder> - the replacement is Group 1 + <placeholder> string
See the online demo and the regex demo:
#!/bin/bash
s='"example1_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example2_policy" {
name="example2"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example3_policy" {
name="example3"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}'
perl -0777 -pe 's/^(\h*policy=)[^()]*(\((?:[^()]++|(?2))*\))/$1<placehlder>/gm' <<< "$s"
Output:
"example1_policy" {
name="example"
policy=<placehlder>
}
"example2_policy" {
name="example2"
policy=<placehlder>
}
"example3_policy" {
name="example3"
policy=<placehlder>
}
You could do this quite easily in Python since you have state:
def clean(s, pattern='policy='):
pre, post = s.split(pattern)
harmony = True
quote = False
braces = 0
for i, char in enumerate(post):
if harmony and char == '\n':
return f'{pre}{pattern}{post[i:]}'
if not quote:
if char == '(':
braces += 1
elif char == ')':
braces -= 1
elif char in ('"', "'"):
quote = char
harmony = braces == 0
elif quote == char:
quote = False
This would even ignore braces that are enclosed in strings (both " and ' strings).
So, this version works on trickier strings too:
"example_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Al)l'ow"
},
]
Version='"2012-10)-17"'
}
)
}
You can easily extend this to support other types of braces or quotation. The only difference is that you need to use counters for braces since the opening and the closing characters are different, while for quotations you just need to add extra characters to the matching list - since everything else within the quotation is ignored, you just need to remember which character the quote was opened with.
Doing this with regexes would be tricker since they only support finite brace nesting.
To remove multiple policies within the same string we need to define a helper function:
def clean(s):
harmony = True
quote = False
braces = 0
for i, char in enumerate(s):
if harmony and char == '\n':
return s[i:]
if not quote:
if char == '(':
braces += 1
elif char == ')':
braces -= 1
elif char in ('"', "'"):
quote = char
harmony = braces == 0
elif quote == char:
quote = False
return s
And then the main function that will apply the "cleaning helper" to each individual chunk:
def clean_all(s, pattern='policy='):
head, *tail = s.split(pattern)
return f'{head}{pattern}{pattern.join(clean(part) for part in tail)}'

shell script,how to print the newline in default position always?

i am using tput sc/rc/ed and printf '\E[n<A|B|C|D>'|printf '\E[y;xH',here two ex:
tty_esc(){ printf "\e[%s" "$1"; }
tty_cursor_locate(){ tty_esc "${2};${1}H"; }
tty_cursor_right(){ tty_esc ${1}C; }
print_center()
{
local _width=$(tput cols)
local _str=$1
local _row=$2
local _cols=$((((${_width} - ${#_str})) / 2))
tty_cursor_locate ${_cols:-100} ${_row:-1}
printf "%s\n" " ${_str} "
}
show_net_adapter()
{
local _addr _iface _count
local _origin=$1
iface_line_count=
tty_cursor_locate ${_origin:-0} 4
printf "%s\n" "Current connected adapter(s):"
for _iface in $(get_net_adapter);do
if [[ "${_iface}" != "lo" ]];then
_addr=$(get_net_addr ${_iface})
test -z "${_addr}" && continue
let _count+=1
let iface_line_count+=1
if [[ ${_count} != 1 ]];then
unset _count
printf '%s' "${tty_rever}"
fi
tty_cursor_right ${_origin:-0}
print_fill 50 ${_iface} ${_addr:--}
printf "${tty_reset}"
fi
done
print_line -s "=" ${line_origin}
}
as above, I should locate the cursor before I print something.
BTW
I use trap "myfunc" WINCH, it only works once. when I try again to change my crt. size, it doesn't work.
Not really sure what you mean by in default position always, either intentations or overwrite the text but the following function can do both.
#!/bin/sh
preent () { # $1=indent start line, $2=spaces, $3=reset cursor, $4=update (1=itself, 2=delete previous lines, 0=newline), $5=save cursor, $6=text
if text="$6""$7""$8""$9""${10}" awk -v col="$COLUMNS" -v cp="$_CURSOR_POS" -v id="$1" -v spc="$2" -v rc="$3" -v u="$4" -v sc="$5" '
BEGIN { t=ENVIRON["text"]; lt=sprintf("%d",col/4); idx=1; len=length(t); y=0; sp=sprintf("% " spc "s",""); delete A;
for(ln=1;idx<len;ln++) { if(ln==id) { col=col-spc; y=1 } bd=col-lt; f=0
for(i=idx+col-1;i>bd;i--) { c=substr(t,i,1);
if(c==" ") {
if(y) { A[ln]=sprintf("%s%s",sp,substr(t,idx,i-idx)); idx=i+1; f=1; break
} else { A[ln]=substr(t,idx,i-idx); idx=i+1; f=1; break }
} else if(c=="") {
if(y) { A[ln]=sprintf("%s%s",sp,substr(t,idx,i-idx)); idx=i; f=1; break
} else { A[ln]=substr(t,idx,i-idx); idx=i; f=1; break; }
}
}
if(!f) {
if(y) { A[ln]=sprintf("%s%s",sp,substr(t,idx,col)); idx=idx+col
} else { A[ln]=substr(t,idx,col); idx=idx+col }
}
} if(rc=="true") cp=0;
if(u=="1") { for(i=1;i<ln;i++) printf("\x1B[1A\x1B[K"); cp=cp+1-i
} else if(u=="2") { for(i=0;i<cp;i++) printf("\x1B[1A\x1B[K"); cp=0 }
for(i=1;i<ln;i++) printf("%s\n",A[i]);
if(sc=="true") { exit ln-1+cp } else { exit 0 }
}'; then _CURSOR_POS="$?"; else _CURSOR_POS="$?"; fi
}
Example 1 (Long text, 5 spaces indent, indent starts from 2nd line)
preent 2 5 'false' 0 'false' 'You never say good-bye Handong-an monghani udukoni anja Dashi saenggakhatjiman ' \
'Momchul sun optkesso Ontong kudae saenggak hal subakke omnun ' \
"Nae jashini miwo Don't you let me go Baby don't you let me down"
Output
You never say good-bye Handong-an monghani
udukoni anja Dashi saenggakhatjiman Momchul
sun optkesso Ontong kudae saenggak hal
subakke omnun Nae jashini miwo Don't you
let me go Baby don't you let me down
Example 2 (Long text with no spaces)
preent 2 5 'false' 0 'false' 'Youneversaygood-byeHandong-anmonghaniudukonianjaDashisaenggakhatjiman' \
'MomchulsunoptkessoOntongkudaesaenggakhalsubakkeomnun' \
"NaejashinimiwoDon'tyouletmegoBabydon'tyouletmedown"
Output
Youneversaygood-byeHandong-anmonghaniudukonianjaD
ashisaenggakhatjimanMomchulsunoptkessoOntong
kudaesaenggakhalsubakkeomnunNaejashinimiwoDo
n'tyouletmegoBabydon'tyouletmedown

I need to create a file with all hashicorp vault key value pairs data using shell script

I need to create a file with all hashicorp vault key value pairs data using shell script.
I want to dump all the data from vault to a flat file.
please advice best way to do it.
Thanks in advance
Prudhvi
Just for keys and values you can use my little Perl script 'vault-backup', that also freezes the data using the correct vault commands.
Please note that this does NOT create a full backup of your Vault! There are no methods being backed up, or any other (unlistable) stuff outside the secrets. It's only usable for simple keys and values. It also probably isn't usable for multiline or binary values. You can patch the script to support that, if you like. ;)
#!/usr/bin/perl
#
# Usage: vault-backup [<PATH> [stdout]]
use Data::Dumper;
use Storable qw(freeze thaw);
# Set vault environment variables
# Always end with a " && " for the actual command
my $setenv =
"VAULT_ADDR=https://myvault.somewhere.com:8200 && ".
"VAULT_CA_PATH=/etc/yourcertificates/ && ";
my $path = $ARGV[0] || "secret/";
if ($path!~/\/$/) {
$path="$path/";
}
push #list, getData($path);
if ($ARGV[1] eq "stdout") {
print Dumper(\#list);
} else {
my $fn="vault-backup-frozen-".time().".dat";
open W,">$fn";
print W freeze(\#list);
close W;
print STDERR "Wrote data to $fn\n";
}
sub getData {
my $path=shift;
print STDERR "Starting getData($path)\n";
my #ret=();
my $command="$setenv vault kv list -tls-skip-verify $path | tail -n+3 ";
print STDERR "starting command: $command\n";
my #lines = `$command`;
chomp #lines;
foreach my $line (#lines) {
if ($line=~/\/$/) {
my #result = getData($path.$line);
if (scalar(#result)>0) {
# Find deeper results
push #ret, #result;
} else {
# empty final dir, no values
push #ret, { path => $path.$line };
}
} else {
# Found a key!
my $command="$setenv vault kv get -tls-skip-verify $path$line";
print STDERR "starting command: $command\n";
my $values = `$command`;
push #ret, {path=>$path.$line, value=>$values};
}
}
return #ret;
}
To restore the data, you can use the script below. It handles data only, it does not act on metadata.
#!/usr/bin/perl
# Usage: vault-restore <backup-filename>
use Data::Dumper;
use Storable qw(thaw);
my %all_entries;
# Set vault environment variables
# Always end with a " && " for the actual command
my $setenv =
"VAULT_ADDR=https://myothervault.somewhere.com:8200 && ".
"VAULT_CA_PATH=/etc/mycertificates/ && ";
# Read the data
my $fn = $ARGV[0] || die("I need a filename with the frozen data");
open F,"<$fn";
my #list = #{ thaw(join("",<F>)) };
close F;
print STDERR "Read ".scalar(#list)." entries.\n";
# Process the data
foreach my $entry (#list) {
print STDERR "\n# adding entry -> $entry->{path}\n";
addEntry($entry);
}
foreach my $path (keys %all_entries) {
my $keyvalues="";
foreach my $key (keys %{$all_entries{$path}}) {
my $value=$all_entries{$path}{$key};
$keyvalues.="'$key=$value' ";
}
print STDERR "vault kv put $path $keyvalues\n";
# `$command`;
}
sub addEntry {
my $entry=shift;
my $path = $entry->{'path'};
if ($entry->{'value'}) {
my $values = $entry->{value};
my #list=split("\n", $values);
my $metadata_engage=0;
my $data_engage=0;
foreach my $keyvalue (#list) {
if ($keyvalue=~/==== Metadata ====/) {
$metadata_engage=1;
$data_engage=0;
} elsif ($keyvalue=~/==== Data ====/) {
$metadata_engage=0;
$data_engage=1;
} elsif ($data_engage) {
my ($key,$value)=($keyvalue=~/^([^ ]+) +(.*)$/);
if ($key ne "Key" && $key ne "---") {
# print STDERR "key=$key ; value=$value\n";
$all_entries{$path}{$key}=$value;
} else {
# print STDERR "-- separator\n";
}
}
}
} else {
print STDERR "Found a final but empty path: $path\n";
}
}

Local `commit-msg` doesn't work in conjunction with global `prepare-commit-msg`

When I have my global git hooks directory (which contains just a prepare-commit-msg hook) set up in config, my local commit-msg doesn't run (although the global hook does). However, when I disable the global prepare-commit-msg hook (by commenting out core.hookspath in gitconfig), the local commit-msg hook works just fine.
~/dotfiles/git-hooks/prepare-commit-msg
#!/usr/bin/env bash
pcregrep -Mv '(# Please.*|# with.*|^#$\n(?!#)|^#$(?=\n# On))' $1 > /tmp/msg && cat /tmp/msg > $1
./.git/hooks/commit-msg (Gerrit's hook to add change-id's if necessary, trimmed to remove license comments)
#!/bin/sh
...
unset GREP_OPTIONS
CHANGE_ID_AFTER="Bug|Depends-On|Issue|Test|Feature|Fixes|Fixed"
MSG="$1"
# Check for, and add if missing, a unique Change-Id
#
add_ChangeId() {
clean_message=`sed -e '
/^diff --git .*/{
s///
q
}
/^Signed-off-by:/d
/^#/d
' "$MSG" | git stripspace`
if test -z "$clean_message"
then
return
fi
# Do not add Change-Id to temp commits
if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!'
then
return
fi
if test "false" = "`git config --bool --get gerrit.createChangeId`"
then
return
fi
# Does Change-Id: already exist? if so, exit (no change).
if grep -i '^Change-Id:' "$MSG" >/dev/null
then
return
fi
id=`_gen_ChangeId`
T="$MSG.tmp.$$"
AWK=awk
if [ -x /usr/xpg4/bin/awk ]; then
# Solaris AWK is just too broken
AWK=/usr/xpg4/bin/awk
fi
# Get core.commentChar from git config or use default symbol
commentChar=`git config --get core.commentChar`
commentChar=${commentChar:-#}
# How this works:
# - parse the commit message as (textLine+ blankLine*)*
# - assume textLine+ to be a footer until proven otherwise
# - exception: the first block is not footer (as it is the title)
# - read textLine+ into a variable
# - then count blankLines
# - once the next textLine appears, print textLine+ blankLine* as these
# aren't footer
# - in END, the last textLine+ block is available for footer parsing
$AWK '
BEGIN {
if (match(ENVIRON["OS"], "Windows")) {
RS="\r?\n" # Required on recent Cygwin
}
# while we start with the assumption that textLine+
# is a footer, the first block is not.
isFooter = 0
footerComment = 0
blankLines = 0
}
# Skip lines starting with commentChar without any spaces before it.
/^'"$commentChar"'/ { next }
# Skip the line starting with the diff command and everything after it,
# up to the end of the file, assuming it is only patch data.
# If more than one line before the diff was empty, strip all but one.
/^diff --git / {
blankLines = 0
while (getline) { }
next
}
# Count blank lines outside footer comments
/^$/ && (footerComment == 0) {
blankLines++
next
}
# Catch footer comment
/^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
footerComment = 1
}
/]$/ && (footerComment == 1) {
footerComment = 2
}
# We have a non-blank line after blank lines. Handle this.
(blankLines > 0) {
print lines
for (i = 0; i < blankLines; i++) {
print ""
}
lines = ""
blankLines = 0
isFooter = 1
footerComment = 0
}
# Detect that the current block is not the footer
(footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
isFooter = 0
}
{
# We need this information about the current last comment line
if (footerComment == 2) {
footerComment = 0
}
if (lines != "") {
lines = lines "\n";
}
lines = lines $0
}
# Footer handling:
# If the last block is considered a footer, splice in the Change-Id at the
# right place.
# Look for the right place to inject Change-Id by considering
# CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
# then Change-Id, then everything else (eg. Signed-off-by:).
#
# Otherwise just print the last block, a new line and the Change-Id as a
# block of its own.
END {
unprinted = 1
if (isFooter == 0) {
print lines "\n"
lines = ""
}
changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
numlines = split(lines, footer, "\n")
for (line = 1; line <= numlines; line++) {
if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
unprinted = 0
print "Change-Id: I'"$id"'"
}
print footer[line]
}
if (unprinted) {
print "Change-Id: I'"$id"'"
}
}' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T"
}
_gen_ChangeIdInput() {
echo "tree `git write-tree`"
if parent=`git rev-parse "HEAD^0" 2>/dev/null`
then
echo "parent $parent"
fi
echo "author `git var GIT_AUTHOR_IDENT`"
echo "committer `git var GIT_COMMITTER_IDENT`"
echo
printf '%s' "$clean_message"
}
_gen_ChangeId() {
_gen_ChangeIdInput |
git hash-object -t commit --stdin
}
add_ChangeId
~/dotfiles/gitconfig (trimmed) when global hook is enabled.
...
[core]
editor = code -rwg $1:2
excludesfile = /Users/shreyasminocha/.gitignore
compression = 0
hookspath = /Users/shreyasminocha/dotfiles/git-hooks
...
Git version: 2.18.0
Edit: As #phd pointed out in the comments, "The problem is that global hookspath completely takes over local hooks. If the global hookspath is defined local hooks are never consulted. [I had] to create global /Users/shreyasminocha/dotfiles/git-hooks/commit-msg that [would] run local .git/hooks/commit-msg". Confirming duplicate.
Eventually solved this by adding some code to the global hook, just as #phd suggested. It looks for a local hook and runs it if it exists.
Global prepare-commit-msg hook:
#!/usr/bin/env bash
# if a local hook exists, run it
if [ -e ./.git/hooks/prepare-commit-msg ]; then
./.git/hooks/prepare-commit-msg "$#"
fi
if [ -e ./.git/hooks/commit-msg ]; then
./.git/hooks/commit-msg "$#"
fi
# global_hook-related things follow
pcregrep -Mv '(# Please.*|# with.*|^#$\n(?!#)|^#$(?=\n# On))' $1 > /tmp/msg && cat /tmp/msg > $1

Owner and permissions in debian

I've just installed DEBIAN today and installed XXXXX application.
This application create ~/.XXXXX/ folder configuration.
I find that the ~/.XXXXX folder is owned by root. Applying any change for instance, then closing and reopening XXXXX, should reflect the applied change.
However, any channel or setting change is forgotten immediately.
A simple chown command solves all.
I was a little confused to start with when I couldn't save my settings... but a little digging shows the problem. It's a little annoying, and the only additional application I've installed so far which behaves this way, which kinda singles it out.
Because this application not have maintainer, I want to resolve this problem for another users.
I beleve that the proble is in debian/postinst file:
set -e
setuid () {
db_get XXXXX/setuid
if [ -x /usr/bin/XXXXX ] && [ "$RET" = "false" ] ; then
if [ ! -x /usr/sbin/dpkg-statoverride ] ||\
! /usr/sbin/dpkg-statoverride --list /usr/bin/XXXXX >/dev/null; then
chown root:root /usr/bin/XXXXX
chmod u=rwx,go=rx /usr/bin/XXXXX
fi
else
if [ ! -x /usr/sbin/dpkg-statoverride ] || \
! /usr/sbin/dpkg-statoverride --list /usr/bin/XXXXX >/dev/null; then
chown root:root /usr/bin/XXXXX
chmod u=rwxs,go=rx /usr/bin/XXXXX
fi
fi
}
# Parse the option requested from XXXXX-configure
get_option () {
OPTION=`XXXXX-configure --$1 2>/dev/null | awk -F: '{ print \$2 }'`
}
suck_XXXXX_xml () {
# If XXXXX-configure was not there at Debconf, but
# /etc/XXXXX/XXXXX.xml was, then we presume that the
# administrator knew what he was doing. We will therefore suck
# this information into the Debconf database now that
# XXXXX-configure is installed.
if [ -f /etc/XXXXX/debconf.XXXXX.xml ]; then
get_option norm
db_set XXXXX/norm "$OPTION"
get_option frequencies
db_set XXXXX/frequencies-ntsc "$OPTION"
db_set XXXXX/frequencies-jp "$OPTION"
db_set XXXXX/frequencies-pal "$OPTION"
get_option device
db_set XXXXX/v4ldevice "$OPTION"
get_option vbidevice
db_set XXXXX/vbidevice "$OPTION"
get_option priority
db_set XXXXX/processpriority "$OPTION"
fi
}
### MAIN POSTINST ###
case "$1" in
configure)
# Load Debconf library
. /usr/share/debconf/confmodule
# Handle the setuid bit.
setuid
# Load /etc/XXXXX/XXXXX.xml into Debconf, if necessary
suck_XXXXX_xml
# Try to set the /var/run/XXXXX directory to video group
if [ -d /var/run/XXXXX ] ; then
if [ ! -x /usr/sbin/dpkg-statoverride ] || \
! /usr/sbin/dpkg-statoverride --list /var/run/XXXXX >/dev/null; then
chmod ug=rwx,o=rxt /var/run/XXXXX
chown root:video /var/run/XXXXX
fi
fi
CONFIGFILE=/etc/XXXXX/XXXXX.xml
db_get XXXXX/norm
NORM=$RET
case "$NORM" in
NTSC|PAL-M|PAL-Nc)
db_get XXXXX/frequencies-ntsc
case "$RET" in
Cable) FREQTABLE=us-cable ;;
Broadcast) FREQTABLE=us-broadcast ;;
*) FREQTABLE=us-cable100 ;;
esac
;;
NTSC-JP)
db_get XXXXX/frequencies-jp
case "$RET" in
Cable) FREQTABLE=japan-cable ;;
*) FREQTABLE=japan-broadcast ;;
esac
;;
PAL|PAL-60|PAL-N|SECAM)
db_get XXXXX/frequencies-pal
case "$RET" in
Europe) FREQTABLE=europe ;;
France) FREQTABLE=france ;;
Russia) FREQTABLE=russia ;;
Australia) FREQTABLE=australia ;;
"New Zealand") FREQTABLE=newzealand ;;
"China Broadcast") FREQTABLE=china-broadcast ;;
"Australia Optus cable") FREQTABLE=australia-optus ;;
*) FREQTABLE=custom ;;
esac
;;
*)
FREQTABLE=custom
;;
esac
db_get XXXXX/v4ldevice
V4LDEV=$RET
db_get XXXXX/vbidevice
VBIDEV=$RET
db_get XXXXX/processpriority
PRI=$RET
db_stop
# Create the configuration file if it doesn't exist
if [ ! -f $CONFIGFILE ]; then
cp /usr/share/doc/XXXXX/examples/default.XXXXX.xml $CONFIGFILE
fi
# Configure XXXXX.
XXXXX-configure --configfile="$CONFIGFILE" --norm="$NORM" \
--frequencies="$FREQTABLE" --device="$V4LDEV" \
--vbidevice="$VBIDEV" --priority="$PRI" 2>/dev/null
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
Any help is welcome,
Regards.
From app code
int mkdir_and_force_owner( const char *path, uid_t uid, gid_t gid )
{
if( mkdir( path, S_IRWXU ) < 0 ) {
if( errno != EEXIST ) {
lfprintf( stderr, _("Cannot create %s: %s\n"),
path, strerror( errno ) );
} else {
/* It exists, make sure it's a directory. */
DIR *temp_dir = opendir( path );
if( !path ) {
lfprintf( stderr, _("Cannot open %s: %s\n"),
path, strerror( errno ) );
} else {
closedir( temp_dir );
return 1;
}
}
} else {
/* We created the directory, now force it to be owned by the user. */
if( chown( path, uid, gid ) < 0 ) {
lfprintf( stderr, _("Cannot change owner of %s: %s.\n"),
path, strerror( errno ) );
} else {
return 1;
}
}
return 0;
}
ct->uid = getuid();
/* Make the ~/.XXXXX directory every time on startup, to be safe. */
if( asprintf( &temp_dirname, "%s/.XXXXX", getenv( "HOME" ) ) < 0 ) {
/* FIXME: Clean up ?? */
return 0;
}
mkdir_and_force_owner( temp_dirname, ct->uid, getgid() );
free( temp_dirname );
This is main app with create initial directory in home. Drop root privileges as early as possible. Only re-enable them to get access to the real-time scheduler. This not work in debian.
int main( int argc, char **argv )
{
rtctimer_t *rtctimer = 0;
int read_stdin = 1;
int result = 0;
int realtime = 0;
uid_t priv_uid = geteuid();
uid_t user_uid = getuid();
/*
* Temporarily drop down to user-level access, so that files aren't
* created setuid root.
*/
if( seteuid( user_uid ) == -1 ) {
lfprintf( stderr, _("\n"
" Failed to drop root privileges: %s.\n"
" motv will now exit to avoid security problems.\n\n"),
strerror( errno ) );
return 1;
}
setup_i18n();
setup_utf8();
lfprintf( stderr, _("Running %s.\n"), PACKAGE_STRING );
/* Steal system resources in the name of performance. */
/* Get maximum priority before dropping root privileges. We'll drop back */
/* to the value specified in the config file (or the default) later. */
seteuid( priv_uid );
setpriority( PRIO_PROCESS, 0, -19 );
if( set_realtime_priority( 0 ) ) {
realtime = 1;
}
rtctimer = rtctimer_new( 0 );
if( rtctimer ) {
if( !rtctimer_set_interval( rtctimer, 1024 ) &&
!rtctimer_set_interval( rtctimer, 64 ) ) {
rtctimer_delete( rtctimer );
rtctimer = 0;
} else {
rtctimer_start_clock( rtctimer );
if( rtctimer_get_resolution( rtctimer ) < 1024 ) {
rtctimer_delete( rtctimer );
rtctimer = 0;
}
}
}
/* We've now stolen all our root-requiring resources, drop to a user. */
if( setuid( user_uid ) == -1 ) {
/*
* This used to say "Unknown problems", but we're printing an
* error string, so that didn't really make sense, did it?
*/
lfprintf( stderr, _("\n"
" Failed to drop root privileges: %s.\n"
" motv will now exit to avoid security problems.\n\n"),
strerror( errno ) );
return 1;
}
/* Ditch stdin early. */
if( isatty( STDIN_FILENO ) ) {
read_stdin = 0;
close( STDIN_FILENO );
}
/* Run motv. */
for(;;) {
if( result == 2 ) {
char *new_argv[ 2 ];
new_argv[ 0 ] = "motv";
new_argv[ 1 ] = 0;
result = motv_main( rtctimer, read_stdin, realtime, 0, new_argv );
} else {
result = motv_main( rtctimer, read_stdin, realtime, argc, argv );
}
if( result != 2 ) break;
}
if( rtctimer ) {
rtctimer_delete( rtctimer );
}
return result;
}
The problem is not created by the debian package postinst script because it never creates the ~/.XXXXX directory.
Check the values of ct->uid and getgid() when you call mkdir_and_force_owner(): that function is not called on your main() so I cannot read how you are calling it.
You have to be sure that they are not 0 and 0: print the values for debug. In this case the function will create and chown() the dir root:root; BTW you are running the software as root due to the setuid bit, otherwise you couldn't chown() your configuration dir.

Resources