Showing posts with label hacks. Show all posts
Showing posts with label hacks. Show all posts

Friday, 13 January 2017

Gentoo adventures: running a script after a package is updated / installed (emerge hook)

So here's a neat little thing that I learned tonight: how to run a script after a specific package is installed or updated.

Why?

Because I use the net-fs/cifs-utils package to provide mount.cifs and I'd like scripts run by an unprivileged user to be able to mount and dismount entries in /etc/fstab. I'd also like user-space tools like the Dolphin file manager to be able to mount these points.

At some point, there was an suid USE flag for net-fs/cifs-utils but it was decided that the USE flag be removed due to security concerns. Which is probably a good decision to have as a default, but it's a little annoying when my script to sync media to my media player fails because it can't (re-)mount the samba share after net-fs/cifs-utils was updated.

I had a hunch that there would be a way to hook into the emerge process -- and Gentoo (or rather, Portage) didn't let me down. After finding https://dev.gentoo.org/~zmedico/portage/doc/portage.html#config-bashrc-ebuild-phase-hooks (which alluded to creating bash functions in a special file) and https://wiki.gentoo.org/wiki/Handbook:X86/Portage/Advanced#Using_.2Fetc.2Fportage.2Fenv which hinted at where to do such magic, and after a little play-testing, I finally got to adding the following function to the (new) file /etc/portage/bashrc:


function post_pkg_postinst() {
  if test "$CATEGORY/$PN" = "net-fs/cifs-utils"; then
    TARGET=/sbin/mount.cifs
    echo -e "\n\n\e[01;31m >>> Post-install hook: setting $TARGET SUID <<<\e[00m\n\n"
    chmod +s $TARGET
  fi
}


Which automatically sets the SUID bit and gives me a giant red warning that it did so.

https://wiki.gentoo.org/wiki/Handbook:X86/Portage/Advanced#Using_.2Fetc.2Fportage.2Fenv  seemed to suggest that I could create the hook in /etc/portage/env/net-fs/cifs-utils/bashrc, but that didn't work out for me (EDIT: putting stuff under there is a very bad idea unless you're only setting environment variables -- I initially put my hooks under there and found that subsequent emerges installed under /etc/portage/env !!) . The above did and was (reasonably) simple to implement, after a few install attempts and dumping the environment that the script runs in with the env command.

Refactoring time: I'd like to be able to write individual scripts for packages (if I ever do this again), like I do with USE flags, where I store all flags pertinent to the installation of a higher-order package in /etc/portage/package.use/higher-order-package so I know where lower-down USE flags come from, for example my /etc/portage/package.use/chromium file contains:

www-client/chromium -cups -hangouts
>=dev-libs/libxml2-2.9.4 icu
>=media-libs/libvpx-1.5.0 svc cpu_flags_x86_sse4_1 postproc


Wherein I can always reminisce about how I had to set the icu flag on libxml2 to successfully install chromium (https://wiki.gentoo.org/wiki/Qt/FAQ#qtwebkit_vs_chromium_block_caused_by_icu)

Similarly, it would be nice if I could just create code snippets under /etc/portage/env/${CATEGORY}/${PN}, perhaps even have category-wide scripts (which an individual package could override)... hm. Ok, so first, we move the script above to /etc/portage/env/net-fs/cifs-utils/post-install. Then we create a new /etc/portage/bashrc with these contents:

PORTAGE_HOOKS_PREFIX=/etc/portage/hooks
PACKAGE_HOOKS_DIR="$PORTAGE_HOOKS_PREFIX/$CATEGORY/$PN"
CATEGORY_HOOKS_DIR="$PORTAGE_HOOKS_PREFIX/$CATEGORY"
function source_all() {
  if test -d "$1"; then
    for f in $1/*; do
      if test -f "$f"; then
        source "$f"
      fi
    done
  fi
}
source_all "$CATEGORY_HOOKS_DIR"
source_all "$PACKAGE_HOOKS_DIR"


Which, as suggested above, loads all scripts from the category folder (if it exists) then overlays with scripts from the package folder (if that exists). So now it's trivial to add hooks for any of the emerge phases outlined in https://dev.gentoo.org/~zmedico/portage/doc/portage.html#config-bashrc-ebuild-phase-hooks

Side-note: since the scripts above can change any environment variable involved in the build, it's quite important to use nice, long, specific variable names. One of my first attempts used PREFIX instead of PORTAGE_HOOKS_PREFIX and that just ended up installing all new packages under the /etc/portage/hooks directory because, of course, $PREFIX is an environment variable respected by most build scripts... *facepalm*.

Once again, this becomes a testament to how Gentoo provides an operating system which is totally yours. You have the control to deal with all of it, its state is as a result of your actions and choices. Once again, I wish I'd switched years ago.

Friday, 30 October 2015

Razor views and publishing woes



If you've done any ASP.NET MVC work, especially in VS2015, you may have encountered this:

  •  You’re devving away on a controller and return View(withSomeNiftyModel).
  • You alt-enter or alt-. to get VS to create the view
  • You make some amazing html view. It has tags and content and everything!
  • You test the view using an F5 or Ctrl-F5 and it’s doing what you want and looking how you want (though your designer, who may or may not be named Jess, probably has sterner words about how it looks)
  • Some time later, you build a deployment package and upload to the client.
  • If the internet gnomes are with you, soon you will deploy at the client. If not, you’ll wait… and then deploy at the client
  • And then you get the dreaded “view not found error page”:







Well, you don’t have to continue with this sad existence. You could add this snippet to your web project’s .csproj file:

  <Target Name="EnsureContentOnViews" BeforeTargets="BeforeBuild">
    <ItemGroup>
      <Filtered Include="@(None)" Condition="'%(Extension)' == '.cshtml'" />
    </ItemGroup>
    <Error Condition="'@(Filtered)'!=''" Code="CSHTML" File="$(MSBuildProjectDirectory)\%(Filtered.Identity)" Text="View is not set to [BuildAction:Content]" />
  </Target>

(Place it after the ItemGroup which contains your views, if you’re looking for a spot to put it)

And then, voila! Your project doesn’t build unless all views are set to build action content:



Kudos to the solution here: http://stackoverflow.com/questions/27954267/make-sure-all-cshtml-files-are-set-to-be-content-for-build-action (though I’ve upgraded from a warning to an error, because, well, it should be).


Wednesday, 21 January 2015

EntityFramework: getting to the bottom of a murky error

In doing some work using EF which talks to existing databases on MSSQL in production and SQLCE for testing (check out https://github.com/fluffynuts/OrmNomNom.git for an example of using SQLCE and a transient database to do Entity testing -- there's also a bit in there about using NHibernate too), I used the Entity POCO generation tools to quickly spew out the classes that should work when talking to the existing database. I also wrote some FluentMigrator migrations so I could get a temporary database up to speed and set off to use that method to test some database interaction, when:
Boom! InvalidOperationException ("Sequence contains no elements").
Now the befuddling aspect of this exception is where it was thrown. I basically have some code like:
using (var ctx = new DragonContext(db.CreateConnection()))
{
    var unit = new SupplierPriceList() 
    { 
        SupplierPriceListID = "test", 
        SupplierID = "foo", 
        MaterialID = "foo", 
        CompanyCode = "foo", 
        Date = DateTime.Now, 
        SupplierPrice = 1.23
    };
    ctx.SupplierPriceLists.Add(unit);
    ctx.SaveChanges();
}
And the exception is thrown at the point of the call to Add(). Which doesn't seem to make a lot of sense as the temp database being used has no rows in any tables, so of course SupplierPriceLists is empty and one might expect that to be the source of the exception, considering the text, but it really shouldn't matter. Tables are empty all the time and we can add rows to them. Still, the error befuddles...
Looking at the stack trace, I see the last two frames are:
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
   at System.Data.Entity.Utilities.DbProviderManifestExtensions.GetStoreTypeFromName(DbProviderManifest providerManifest, String name)
Ok, so the last frame explains the raw reason for the exception: something is doing a Single() call on an empty collection. But what? The call one up looks interesting: GetStoreTypeFromName seems to suggest that Entity is doing a lookup to find out how it should store the data for that property. So, on a hunch, comment out all properties except the implied key field (SupplierPriceListID) and the Add() call works. Hm. Perhaps SQLCE doesn't like the
double?
fields? Nope -- uncomment them and things still work.
Then the culprit leaps out. The generator tool has annotated a DateTime? field with:
[Column(TypeName = "date")]
Removing the annotation causes the error to subside -- and the SaveChanges() call on the context passes, on both MSSQL and SQLCE
I thought I'd just make a record of this here as I found nothing particularly useful through much googling and eventually found and resolved the problem based on a hunch. Perhaps this post can save someone else a little mission. Of course the DateTime? value is truncated for the Date field, but hey, that's what the client originally intended...

What's new in PeanutButter?

Retrieving the post... Please hold. If the post doesn't load properly, you can check it out here: https://github.com/fluffynuts/blog/...