In summer 2017 I was on a four-week trip through the beauty of Iceland.
The incredible nature, the cold lava, the Geysir, the mountains and rivers - the awesome weather we had on almost every day made me fall in love with this beautiful country.
On Sunday, 23th of July 2017 my journey began. I arrived in Kevlavik late at night. My plane landed at 23:55 (without delay). Awesome time to simply fall into the bed and get a few hours of sleep, right? But unfortunately I had to find my luggage first. And that took me 1 hour, despite Kevlavik Airport beeing rather small. After checking the arrival hall for half an hour, I asked the office for lost luggage what to do, because four weeks of Iceland without fresh underwear would be ... not that pleasant.
The Lady simply told me that she couldn't do anything and I have to wait for about two days and then I'll get an email from the airline and they will send my luggage to my address. Beeing on the road for four weeks, I told her that I have no address in Iceland. She told me my luggage will then be send to my home country.
Yeah. Thank you.
After that I left. On my last walk through the hall, though, I found my luggage in the place for “odd sized luggage”. Vacation saved.
The first day was okayish at best. A bit of rain, a bit of wind (for germans actually a lot of rain and wind, but I guess it was rather good weather for Icelandic people). But the weather got a lot better on my 3rd and 4th day. We had sun, beautiful blue sky and rather nice temperatures.
We visited the Geysir in the South-West rather early on.

We visited Geysir in the early morning where all the (other) tourists were still in bed. Because of that we were alone at Geysir – definitively better than getting overrun!
We also were at the Gullfoss, which was also spectacular. But after that we escaped the masses and went into the highlands, where the next picture was taken.

The day after that picture was taken, we drove from Hvítárvatn to Ásgarður (Kerlingarfjöll) where we hike from the campingside to the hot Springs. It smelled aweful, because the springs are sulfur-springs. But we had an awesome day, about 20,000 steps (roughly 14 km) on my counter and we took awesome pictures.
The West-Fjords were awesome. We had awesome hot-pots to take a break in, we met awesome people from Australia and Canada and we had the opportunity to take awesome pictures. During our stay in the West-Fjords we had to drive from one side of the Fjords to the other, which we did with route F66. Holy crap, that was an adventure by itself! First, the road was nothing more than a track next to a dry river. Later, it was barely a track anymore, just an approximation of ways through the landscape. Our mobilehome had to suffer through it, and so had we. But we made it (thanks goes out to the two awesome ladies in the van behind us, who kept a certain distance so we didn't get into a hurry and made a consequential mistake or something).
We met some fellow german travelers in Tálknafjörður at the camping side, which was also really nice.
A few kilometers later we arrived at the most western point of Europe. We took photos of the puffins there and enjoyed the loneliness on the campingside, where we stayed two days.

It got cold quickly in the evening, but we enjoyed the awesome weather during the day there and with a big cup of hot tea, one can enjoy the evenings there as well.
One of the tourist-highlights one has to see in Iceland is the Askja. The Askja is an old vulkan crater which is filled with water. It is in the middle-east of Iceland, north of the Vatnajökull national park. But before we got there, we had to cross the biggest desert in Europe: The black sand desert, I believe it belongs to Vatnajökull national park. Google maps does not even have roads for this part of Iceland.

After driving about 100 km per day for 3 days (yes, that's a 8-10 hour drive each day, with about 10-15 km per hour), we arrived at Askja in not-so-optimal weather. But it was worth every minute, even if it was hard.
In the east, we visited some friends and a black-sand beach, which was an amazing experience. Unfortunately it was to cold to swim in the sea.

In the evening, we even saw nordic lights. Not that many, to be honest, because it was rather cloudy, but we definitively saw them! How amazing!
After our short visit, we departed from Seyðisfjörður harbour to Färöer Islands, Tórshavn.
On our trip back home, we had a four-day stop at Färöer Islands. We arrived in Tórshavn at night and drove until we found a nice parking space to sleep. On the next day we drove up north. Färöer is a really small country, but as beautiful as Iceland itself. I would even say that I fell in love again, despite seeing very little of the country.
Our journey continued on day 4, from Tórshavn to Hirtshals.
After arriving in Hirtshals and driving south towards the german border, we noticed that germans are just crazy. The roads were packed with cars the moment we crossed the border.
Our four-day-trip from Hirtshals to our home was not fun anymore. After weeks of peace and beauty in the rough but awesome nature of Iceland, we concluded that germans are the worst.
I hope I can come back to Iceland one day. As we missed the nordic lights just by a few days, it would be awesome to get another chance for seeing them.
Merry Christmas and a happy 33c3!
Last year on christmas I posted a blog post as well, so I figured I'll do it again this year.
This year, though, I have a little present for you all and myself. This blog got a new design and is also built with another tool. Why is that? Well, because I wanted to change something. So I ditched frog and opted for hugo.
Don't get me wrong, frog is an awesome tool and I really love it! Racket is a great language and I hoped to get my feet wet with it a bit while using frog. I didn't. Sadly.
But hugo is faster. And it got to the point where speed matters. I didn't want to use nanoc, though I know it and I've built some sites with it already (starting with my wiki.template which was initially built with nanoc and the imag-pim.org website which is build with nanoc). I wanted to use something new. And a quick query to staticgen.com told me that jekyll, hugo and gitbook were my options. Yes, there are more engines – I considered middleman as well. But no, I wanted something new. So hugo it is.
By now I'm amazed. It is really fast, all the things just work and the template I chose was almost perfect for my needs.
I did some work to make the URLs backward compatible, so all your links should work as expected. If not, tell me, I will fix it. What changed, though, is the location of the RSS file. So you have to use the new URL: RSS.
So here we go.
Have a nice Christmas eve. See you at 33c3!
This is the 21th iteration on what happened in the last four weeks in the imag project, the text based personal information management suite for the commandline.
imag is a personal information management suite for the commandline. Its target audience are commandline- and power-users. It does not reimplement personal information management (PIM) aspects, but re-uses existing tools and standards to be an addition to an existing workflow, so one does not have to learn a new tool before beeing productive again. Some simple PIM aspects are implemented as imag modules, though. It gives the user the power to connect data from different existing tools and add meta-information to these connections, so one can do data-mining on PIM data.
Well, as I said before, I have a really busy masters degree to do, so progress in imag slowed down a lot. Believe me or not, I don't like that. I want to continue to work on imag as much as possible, but right now, I just do not have enough time for it.
I have a plan, though.
But first, lets see what happenend in the last four weeks.
The following issues and PRs were touched in the last weeks:
imag-mailclap versionimag-mailimag link internal --listMakefile support for cargo-outdatedWell, I cannot predict the future. As Christmas (and 33c3) is coming up, I don't know how much time I will have to work on imag.
But what I want to do: Shift my focus a bit. Before I had a kind of Shotgun-Approach: Implement everything, work on multiple things all the time. I will not do this anymore. In fact, I will move issues from the 0.3.0 release to 0.4.0 and so on, shift everything by one release. That will free the 0.3.0 release and I will focus on one goal in this release then: A scripting API.
I want to develop an imag library for writing scripts for imag. I think about Ruby, because that's a language I am familiar with. Another option would be Lua. I hope I can construct the scripting-API interface in a way so it can be re-used to write a Lua interface.
But that will it be for 0.3.0. Having a neat scripting API (not low-level but rather high-level) gives us the possibility to quickly write simple integrations with tools, for example to integrate mails, a wiki or something similar.
I hope that's a plan. 0.3.0 is due to March 2017 as far as I remember, so there's plenty of time for this.
My next post will be around mid-january 2017, so I think it is appropriate to wish you all a very fine Christmas and a good new year!
tags: #linux #open source #programming #rust #software #tools #imag
Another 14 days vanished quickly as hell.
Read what happened in the imag codebase in the last 14 days, in this 20th iteration on whats coming up in imag, the text based personal information management suite for the commandline.
Also, I have some changes to this series of blog posts. Read about it here as well.
imag is a personal information management suite for the commandline. Its target audience are commandline- and power-users. It does not reimplement personal information management (PIM) aspects, but re-uses existing tools and standards to be an addition to an existing workflow, so one does not have to learn a new tool before beeing productive again. Some simple PIM aspects are implemented as imag modules, though. It gives the user the power to connect data from different existing tools and add meta-information to these connections, so one can do data-mining on PIM data.
In the past two weeks, we got almost nothing done, honestly. We had four pull requests and no new issues. Why is that? Well, most of the PRs are mine as well as most of the issues are opened by me. And I have a hell lot to do with my masters degree. Also, I want to write pull requests to the rust-vobject crate to give it a high-level interface to be able to write my calendar and contacts module with it. This is, as you might think, a nontrivial task and therefor take some time as well.
We had one new contributor though. Thanks to Steven Allen for PR #834.
That's why we didn't have that much progress. I will sum it up here:
libimagutil where I opened one file twice. Thanks again, Steven Allen, for
the contribution!EntryHeader (the querying by string in the header
toml data structure) to a new libimagstore module, so the module
libimagstore::store does only contain a minimal EntryHeader type for
backwards compatibility.EntryHeader type completely and replaces it by toml::Value, its inner
type. This is not completed at the time of writing.Lets see what will happen in the next four weeks.
Yes, that's right. I will do...
I will slow down this blogging series. This was the 20th iteration on the subject, so I already do this for 40 weeks now. I will now slow this down and only post every four weeks about imag. Partly because my masters degree is really time consuming, partly because I already planned this beforehand. Every four weeks will be enough, I guess. I'm not sure whether I will continue include all PRs and issues or only the major ones. We'll see.
For the next four weeks I plan to get some functionality into
rust-vobject.
I will then start to implement imag-contact and I hope to get some progress
with imag-mail, but I cannot promise anything.
tags: #linux #open source #programming #rust #software #tools #imag
Another 14 days vanished quickly as hell.
Read what happened in the imag codebase in the last 14 days, in this 19th iteration on whats coming up in imag, the text based personal information management suite for the commandline.
imag is a personal information management suite for the commandline. Its target audience are commandline- and power-users. It does not reimplement personal information management (PIM) aspects, but re-uses existing tools and standards to be an addition to an existing workflow, so one does not have to learn a new tool before beeing productive again. Some simple PIM aspects are implemented as imag modules, though. It gives the user the power to connect data from different existing tools and add meta-information to these connections, so one can do data-mining on PIM data.
We have more than 150 stars now! Wow!
Besides that we had really not that much progress in the last 14 days. My masters degree keeps me busy all day long and I really have a hard time with all the things I have to do for my courses, so progress slows down way more than I expected. But I can't change that, so ...
v0.2.0 release branch into the master branch.itertools which was removed. We also had to update
task-hookrs as both imag and task-hookrs depend on uuid and they
really should depend on the same version of uuid, so I updated
task-hookrs to the latest uuid, which in fact included some code removal
in the codebase of task-hookrs. This was because uuid depends (and we
also need it) on serde, which was update from 0.7.* to 0.8.*. As serde
included some new awesome things, we were able to remove some code in
task-hookrs.PartialEq
on StoreId, which is a really neat improvement in my opinion. We
derive()d it before, but we now only compare the local part of the
StoreId object (read: The store-part, not the path to the store itself,
which could be absent sometimes, causing weird bugs).Also, two really old PRs were closed. I do not list older PRs normally, but these two are really noteworthy:
clap gets rebuild several times... but at
least cargo doesn't yell at us anymore, so I included it.rustc to 2. I hope we get slightly better build times on travis from
that.imag-* toolsimag binary itself. I did not even think about this as solution, but it
works and I'm willing to merge this as soon as Mario did some cleanup work
in the PR.
What he did is: He include()d all ui.rs files, where we (more or less by
accident) always used the same function name to build the clap:App:
build_ui(App) -> App. He includes these source files via a neat macro as
sub modules and is then able to call all these build_ui functions to build
a App object (with subcommands for each imag module). From that App
object, he can retrieve the bash/fish/zsh completion (at build time). This
is awesome!As far as I see, we had no issues opened in the last 14 days that were also closed within theses two weeks.
Maybe in the next iteration again.
Lets have a quick look what the future might bring us in the imag codebase.
*.sh scripts
we use to test binaries. These scripts really should be rewritten in Rust,
we now have the ability to do tests in-memory, so there shouldn't be any
issues with implementing these things in Rust. It is really a trivial task
(not for a complete rust beginner IMO), so I'll not start implementing this
soon, so others can step up.As said above, I have a lot to do for my masters degree, so I cannot tell how much we can get done in the next two weeks. I'd love to see some progress with the email module, so we can merge this one at least, but I cannot promise it, sadly.
This does not mean I'm not interested in imag anymore. I'm interested even more in it. I just do not have enough time at the moment to do all the things I want to do.
tags: #linux #open source #programming #rust #software #tools #imag
I could not think of a better title, I'm sorry.
These are notes how to create a kernel which can be booted in qemu and pass a drive to it which has loadable kernel modules.
I wanted to play around with the linux kernel and write a minimal filesystem inside the kernel tree. I wanted to boot the kernel in a QEMU, as I do not want to mess with my running kernel.
Here are the steps I did (on a debian VM I run on my universitys infrastructure).
sudo apt-get install build-essential bc git gcc
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
make x86_64_defconfig
make kvmconfig
make menuconfig
The last step brings up the kernel configuration interface, which I used to
enable SATA
(Device Drivers -> Serial ATA and Parallel ATA drivers (libata) -> Generic ATA support)
support which is needed to be able to mount a device into the qemu instance,
where we can put things to play around with in the VM.
I then did a make -j 16 (if you have not 8 cores, use a lower number).
While the kernel compiled, I created a busybox initramfs. I used this blog article for how to do it. I'll dump in the commands from the blog article, for detailed information, go and read it, it's a really good article. I didn't do the speed-optimizing things, as the QEMU boots fast enough for me (~3 seconds).
(I used the latest busybox, but I don't see why it should work with the one from the article, which is about two versions old)
curl http://busybox.net/downloads/busybox-1.23.2.tar.bz2 | tar xjf -
cd busybox-1.23.2
mkdir -pv /tmp/obj/busybox-x86
make O=/tmp/obj/busybox-x86 defconfig
make O=/tmp/obj/busybox-x86 menuconfig # See below why
cd /tmp/obj/busybox-x86
make -j 16
# it does not install in your system, so you can execute this without fear
make install
mkdir -p /tmp/initramfs/x86-busybox
cd /tmp/initramfs/x86-busybox
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
cp -av /tmp/obj/busybox-x86/_install/* .
# now edit the init script
vim init
You have to enable busybox build as static binary (with the menuconfig
part).
The init script I use:
#!/bin/sh
echo "INIT SCRIPT"
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mkdir /tmp
mount -t tmpfs none /tmp
mdev -s # We need this to find /dev/sda later
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
exec /bin/sh
The init script boots into a /bin/sh. We can now build the initramfs with
it:
chmod +x init
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > /tmp/initramfs-busybox-x86.cpio.gz
Now we can try it out, whether the kernel boots and we get out shell:
# In the kernel tree:
qemu-system-x86_64 \
-kernel arch/x86_64/boot/bzImage \
-initrd /tmp/initramfs-busybox-x86.cpio.gz \
-nographic -append "console=ttyS0"
We should see some output like this:
[...]
[ 7.170702] Freeing unused kernel memory: 1200K (ffffffff81f55000 - ffffffff82081000)
[ 7.171539] Write protecting the kernel read-only data: 14336k
[ 7.177328] Freeing unused kernel memory: 816K (ffff880001934000 - ffff880001a00000)
[ 7.219360] Freeing unused kernel memory: 1116K (ffff880001ce9000 - ffff880001e00000)
INIT SCRIPT
mount: mounting none on /sys/kernel/config failed: No such file or directory
mount: mounting none on /tmp failed: No such file or directory
Boot took 7.33 seconds
/bin/sh: can't access tty; job control turned off
/ # [ 7.630681] input: ImExPS/2 BYD TouchPad as /devices/platform/i8042/serio1/input/input3
/ #
(Yes, the output is slightly messed up... but hey! We just booted a linux kernel in a QEMU!
We can now create a kernel module in the linux kernel tree:
# In the kernel tree
git checkout -b my-module
mkdir fs/iamcoolfs/ # because I like filesystems
cat <<EOS > ./fs/iamcoolfs/Kconfig
config COOL_FS
tristate "COOLFS support"
help
Private FS for playing and learning FS programming
This will definitively eat everything you put into it.
EOS
cat <<EOS > ./fs/iamcoolfs/Makefile
#
# Makefile for the linux mb-filesystem routines.
#
obj-$(CONFIG_MB_FS) += coolfs.o
EOS
cat <<EOS > ./fs/iamcoolfs/coolfs.c
#include <linux/init.h>
#include <linux/module.h>
static int __init coolfs_init(void)
{
pr_debug("cool module loaded\n");
return 0;
}
static void __exit coolfs_fini(void)
{
pr_debug("cool module unloaded\n");
}
module_init(coolfs_init);
module_exit(coolfs_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("<YOUR NAME GOES HERE>");
EOS
This creates all the files we need for a minimal module. Because I like
filesystems and I really want to implement a minimal filesystem, I chose to
create the files in ./fs, but I guess it shouldn't matter.
Make sure you adapt the fs/Kconfig file to find your new module! Put
source "fs/coolfs/Kconfig"
into it.
Now you can execute make menuconfig and find the configuration option for
your new module.
We can also simply make M=fs/coolfs to build the module.
Now we create a (ext2) disk where we can cp the module to, so we can mount
that disk into our QEMU and use the module there:
dd if=/dev/zero of=/tmp/disk bs=4M count=100
sudo mkfs.ext2 /tmp/disk # you could also try ext4, for example
mkdir /tmp/disk-mount
sudo mount /tmp/disk /tmp/disk-mount
cp fs/coolfs/coolfs.ko /tmp/disk-mount/
sudo umount /tmp/disk-mount
And when we boot our QEMU now with -hda /tmp/disk, we can find /dev/sda in
the booted instance and mount that:
# In the booted VM
mkdir sda
mount /dev/sda ./sda
ls sda
Happy FS dev!
tags: #c #linux #open source #programming #software #testing #kernel
I managed to break one of my codebase (the master branch) without even
touching it.
If you think about, the “issue” I ran into is trivial. It really is. Though, I want to write about it because often, these things get too little attention in my opinion.
So what did I do?
I maintain this project task-hookrs. The project itself depends on a few other crates. I updated these dependencies and then released a new version of my crate.
The rewrite included some lines which I had to change, not a big deal
actually.
One of my dependencies, namingly serde had
a breaking change from 0.7.* to 0.8.0 which resulted in a small rewrite of a
portion of my code.
No functionality was changed, though.
I released 0.2.2 after the dependencies were upgraded. The last version
before that version was 0.2.1.
And despite me not changing any functionality, the micro-release was a
mistake.
Not because API changes (which I didn't have) but because of link-time
dependencies.
My other project, imag dependes on task-hookrs, but
the dependency specification states task-hookrs = "0.2" (note the missing
.1 at the end).
When the master of imag got rebuild, it failed because imag itself depends
on uuid = "0.2", whereas task-hookrs now depended on uuid = "0.3" – the
linker failed because the types I passed from imag to task-hookrs were
different.
Fuck.
What did I do? Well, I updated uuid in imag as well, everything solved.
But that really got me thinking.
What I should have done: Updating the minor version (0.2.1 -> 0.3.0).
We talk a lot about breaking changes in APIs and such, in fact this is why we introduced semantic versioning in the Rust community. What we do not talk about is that dependencies are also relevant when updating. Even the semantic versioning website states:
Given a version number MAJOR.MINOR.PATCH, increment the:
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
Which clearly states that MAJOR version when you make incompatible API
changes – though nobody tells you that updating a dependency of your library
might also be a incompatible API change!
After all I'm (kinda) safe here, because the semantic versioning spec also contains the following:
Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.
So nobody can blame me. Although this really bugs me and it really shouldn't have happened.
tags: #programming #open source #software #testing
Another 14 days vanished quickly as hell.
Read what happened in the imag codebase in the last 14 days, in this 18th iteration on whats coming up in imag, the text based personal information management suite for the commandline.
imag is a personal information management suite for the commandline. Its target audience are commandline- and power-users. It does not reimplement personal information management (PIM) aspects, but re-uses existing tools and standards to be an addition to an existing workflow, so one does not have to learn a new tool before beeing productive again. Some simple PIM aspects are implemented as imag modules, though. It gives the user the power to connect data from different existing tools and add meta-information to these connections, so one can do data-mining on PIM data.
I released filters in v0.1.1 and
task-hookrs in v0.2.1.
They are now licensed under the terms of MPL-2.0, there were no changes in
functionality besides, in the
filters
library, one can use a filter as an Fn now, but only on nightly rust.
I did not bump the version from v0.1.0 to v0.2.0 because I do not think that
nightly features should be promoted.
They are nice to play with, but stable rust is my target, so I do not include
these things as minor versions.
And, as semver specifies, we are in v0.x.y, so I can do anything I want!
In the last 14 days I got some PRs:
Thank you all very much for your contributions!
imag run.libimaginteraction.libimagannotation, a library for annotating entries (simply a combination
of libimagnotes and libimagentrylink).external linking part of libimagentrylink.There's not much to say about the following list of PRs, so I just list them here.
And there's not that much to say about this issues either... so I simply list them here.
Well, the future. The release is really close. I am still not sure how to properly do the release.
As listed below, we have some discussion in
#814,
and TheNeikos suggested to simply run cargo
publish --no-verify in a loop until all crates are up there. I do not like
this idea very much, though I do not see an alternative. If you have an
alternative, feel free to tell!
For readers that are not familiar with the imag codebase, here's the problem
again:
That means, we have a rather complex net of dependencies of crates in one
repository. We have more than 30 crates there. A release process would need
to find the one crate that does not depend on any other crate from our
repository, cargo publish that one and then walk up the dependency tree
until all crates are published. For this, we would either need to parse the
Cargo.toml files, to find the dependencies, or just loop over all crates and
try to force-publish (as in cargo publish --no-verify) them.
If you have any idea how to do this in a clean and nice (and reproducible) and automated way (I do not want to do this by hand, really), feel free to post a comment on #814!
Some issues were opened that are not yet closed.
git-mv
hook for Store::move* functionslibimagentrylink rewrite to return iterators instead of Vecs.That's it for today, I guess.
tags: #linux #open source #programming #rust #software #tools #imag
Another 14 days vanished quickly as hell.
Read what happened in the imag codebase in the last 14 days, in this 17th iteration on whats coming up in imag, the text based personal information management suite for the commandline.
imag is a personal information management suite for the commandline. Its target audience are commandline- and power-users. It does not reimplement personal information management (PIM) aspects, but re-uses existing tools and standards to be an addition to an existing workflow, so one does not have to learn a new tool before beeing productive again. Some simple PIM aspects are implemented as imag modules, though. It gives the user the power to connect data from different existing tools and add meta-information to these connections, so one can do data-mining on PIM data.
I posted
an article about how I organize my 0.x.y releases.
Now lets see what happened in the past 14 days.
Despite me not beeing on vacation in the last 14 days (surprise!), we didn't manage to get this much done. But at least we got some things done.
imag-mail based
on libimagmail.clap “validator functions” feature, so
they can be used to verify commandline input.readline support
in libimaginteraction, so one can use readline to ask the user for data.README.md
file of the repository.foo.fold(Ok(()),... and replace it by fold_defresult or similar helper
functions we have in the codebase.FromStoreId trait that is defined and used in libimagdiary.libimagentryfilter.libimagstore::store::Store interface to be non-consuming where possible.libimagstore::store::EntryHeader type. This is actually a really
complex issue which might be solved in several PRs rather than in one step.imag mv
feature, with some functionality like mv, but also some functionality to
move store entries. Thank you again,
Julian!I finally started my masters degree this week and I guess I will have a rather high workload in this semester, so I really hope I'm able to do some imag things.
Doing the 0.2.0 release within this year (it is planned to happen before
31.12.2016) should be possible, though we will see whether the 3-month-cycle for
the next releases
I announced in the blog post about my release strategy
will be possible.
tags: #linux #open source #programming #rust #software #tools #imag